diff options
Diffstat (limited to 'private/net/svcdlls/lls/server')
33 files changed, 26072 insertions, 0 deletions
diff --git a/private/net/svcdlls/lls/server/certdb.c b/private/net/svcdlls/lls/server/certdb.c new file mode 100644 index 000000000..e543ad203 --- /dev/null +++ b/private/net/svcdlls/lls/server/certdb.c @@ -0,0 +1,1511 @@ +/*++ + +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 "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; +PLLS_CERT_DB_CERTIFICATE_HEADER CertDbHeaderList = NULL; +DWORD CertDbHeaderListSize = 0; +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 ) + { + // claim does not yet exist; add it + if ( NULL == pHeader->Claims ) + { + pHeader->Claims = LocalAlloc( LPTR, ( 1 + pHeader->NumClaims ) * sizeof( LLS_CERT_DB_CERTIFICATE_CLAIM ) ); + } + else + { + pHeader->Claims = LocalReAlloc( pHeader->Claims, ( 1 + pHeader->NumClaims ) * sizeof( LLS_CERT_DB_CERTIFICATE_CLAIM ), LHND ); + } + + if ( NULL == pHeader->Claims ) + { + // memory allocation failed + pHeader->NumClaims = 0; + } + else + { + // claim list expanded; save server name + 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 ) + { + CertDbHeaderList = LocalReAlloc( CertDbHeaderList, ( 1 + CertDbHeaderListSize ) * sizeof( LLS_CERT_DB_CERTIFICATE_HEADER ), LHND ); + } + else + { + CertDbHeaderList = LocalAlloc( LPTR, ( 1 + CertDbHeaderListSize ) * sizeof( LLS_CERT_DB_CERTIFICATE_HEADER ) ); + } + + if ( NULL == CertDbHeaderList ) + { + // memory allocation failed; bye-bye database! + ASSERT( FALSE ); + CertDbHeaderListSize = 0; + pHeader = NULL; + } + else + { + // 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[ 4 ]; + 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 ); + ASSERT( 0 != cch ); + + 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, + 0, + 0, + pszNextViolationServerEntry, + cbViolationServerList - ( pszNextViolationServerEntry - pszViolationServerList ), + (LPVOID) 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. + +--*/ + +{ + RtlInitializeResource( &CertDbHeaderListLock ); + + CertDbFile = NULL; + + CertDbHeaderList = NULL; + CertDbHeaderListSize = 0; + + 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; + + RtlAcquireResourceShared( &CertDbHeaderListLock, TRUE ); + + // 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; + } + } + + RtlReleaseResource( &CertDbHeaderListLock ); + + 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 diff --git a/private/net/svcdlls/lls/server/certdb.h b/private/net/svcdlls/lls/server/certdb.h new file mode 100644 index 000000000..9dd169c41 --- /dev/null +++ b/private/net/svcdlls/lls/server/certdb.h @@ -0,0 +1,129 @@ +/*++ + +Copyright (c) 1995 Microsoft Corporation + +Module Name: + + certdb.h + +Abstract: + + +Author: + + Jeff Parham (jeffparh) 16-Nov-1995 + +Revision History: + +--*/ + +#ifndef _CERTDB_H_ +#define _CERTDB_H_ + +// maximum time (in seconds) allowed to pass between certificate replications +// before we remove the apparently no longer used data. this is so that, for +// example, if a machine goes down (taking its licenses with it), the licenses +// it had registered won't forever keep the user from reinstalling them. +#define LLS_CERT_DB_REPLICATION_DATE_DELTA_MAX ( 60 * 60 * 72 ) + +#define LLS_CERT_DB_FILE_VERSION ( 0x0201 ) + +typedef struct _LLS_CERT_DB_FILE_HEADER +{ + ULONG ProductStringSize; + ULONG NumCertificates; + ULONG NumClaims; +} LLS_CERT_DB_FILE_HEADER, *PLLS_CERT_DB_FILE_HEADER; + +typedef struct _LLS_CERT_DB_CERTIFICATE_CLAIM +{ + TCHAR ServerName[ 1 + MAX_COMPUTERNAME_LENGTH ]; + DWORD ReplicationDate; + LONG Quantity; +} LLS_CERT_DB_CERTIFICATE_CLAIM, *PLLS_CERT_DB_CERTIFICATE_CLAIM; + +typedef struct _LLS_CERT_DB_CERTIFICATE_HEADER +{ + LPTSTR Product; + DWORD CertificateID; + DWORD MaxQuantity; + DWORD ExpirationDate; + + DWORD NumClaims; + PLLS_CERT_DB_CERTIFICATE_CLAIM Claims; +} LLS_CERT_DB_CERTIFICATE_HEADER, *PLLS_CERT_DB_CERTIFICATE_HEADER; + + +extern RTL_RESOURCE CertDbHeaderListLock; +extern PLLS_CERT_DB_CERTIFICATE_HEADER CertDbHeaderList; +extern DWORD CertDbHeaderListSize; +extern HANDLE CertDbFile; + + +NTSTATUS +CertDbInit(); + +NTSTATUS +CertDbLoad(); + +NTSTATUS +CertDbSave(); + +void +CertDbLogViolations(); + +void +CertDbPrune(); + +void +CertDbRemoveLocalClaims(); + +void +CertDbUpdateLocalClaims(); + +NTSTATUS +CertDbClaimEnter( LPTSTR pszServerName, + PLLS_LICENSE_INFO_1 pLicense, + BOOL bIsTotal, + DWORD ReplicationDate ); + +BOOL +CertDbClaimApprove( PLLS_LICENSE_INFO_1 pLicense ); + +PLLS_CERT_DB_CERTIFICATE_HEADER +CertDbHeaderFind( PLLS_LICENSE_INFO_1 pLicense ); + +PLLS_CERT_DB_CERTIFICATE_HEADER +CertDbHeaderAdd( PLLS_LICENSE_INFO_1 pLicense ); + +int +CertDbClaimFind( PLLS_CERT_DB_CERTIFICATE_HEADER pHeader, + LPTSTR pszServerName ); + +NTSTATUS +CertDbPack( LPDWORD pcchProductStrings, + LPTSTR * ppchProductStrings, + LPDWORD pdwNumHeaders, + PREPL_CERT_DB_CERTIFICATE_HEADER_0 * ppHeaders, + LPDWORD pdwNumClaims, + PREPL_CERT_DB_CERTIFICATE_CLAIM_0 * ppClaims ); + +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 ); + +NTSTATUS +CertDbClaimsGet( PLLS_LICENSE_INFO_1 pLicense, + LPDWORD pdwNumClaims, + PLLS_CERTIFICATE_CLAIM_INFO_0 * ppTargets ); + +#if DBG +void CertDbDebugDump(); +#endif + +#endif diff --git a/private/net/svcdlls/lls/server/llssrv.c b/private/net/svcdlls/lls/server/llssrv.c new file mode 100644 index 000000000..9af40a16a --- /dev/null +++ b/private/net/svcdlls/lls/server/llssrv.c @@ -0,0 +1,1087 @@ +/*++ + +Copyright (c) 1994 Microsoft Corporation + +Module Name: + + Service.c + +Abstract: + + Main routine to setup the exception handlers and initialize everything + to listen to LPC and RPC port requests. + +Author: + + Arthur Hanson (arth) Dec 07, 1994 + +Environment: + +Revision History: + + Jeff Parham (jeffparh) 05-Dec-1995 + o Added certificate database support. + o Expanded file load time (the update limit sent to the service + controller) to account for certificate database loading. + o Reordered initialization such that the license purchase subsystem + is initialized before the service subsystem. (The service + subsystem now uses the license subsystem.) + o Increased internal version number. + +--*/ + +#include <nt.h> +#include <ntlsa.h> +#include <ntsam.h> +#include <ntrtl.h> +#include <nturtl.h> + +#include <windows.h> +#include <stdio.h> +#include <stdlib.h> +#include <process.h> +#include <tchar.h> + +#include <lm.h> +#include <alertmsg.h> + +#include "llsapi.h" +#include "debug.h" +#include "llsutil.h" +#include "llssrv.h" +#include "service.h" +#include "registry.h" +#include "mapping.h" +#include "msvctbl.h" +#include "svctbl.h" +#include "perseat.h" +#include "purchase.h" +#include "server.h" +#include "repl.h" +#include "scaven.h" +#include "llsrpc_s.h" +#include "certdb.h" + + +VOID LLSRpcInit(); +BOOLEAN LLSpLPCInitialize ( VOID ); + + +#define INTERNAL_VERSION 0x0006 + +#define DEFAULT_LICENSE_CHECK_TIME 24 +#define DEFAULT_REPLICATION_TIME 12 * 60 * 60 + +CONFIG_RECORD ConfigInfo; +RTL_CRITICAL_SECTION ConfigInfoLock; + + +VOID LoadAll ( ); + +#if DBG +DWORD TraceFlags = 0; +#endif + +// +// this event is signalled when the service should end +// +HANDLE hServerStopEvent = NULL; +TCHAR MyDomain[MAX_COMPUTERNAME_LENGTH + 2]; +ULONG MyDomainSize; + +BOOL IsMaster = FALSE; + +// +// Files +// +TCHAR MappingFileName[MAX_PATH + 1]; +TCHAR UserFileName[MAX_PATH + 1]; +TCHAR LicenseFileName[MAX_PATH + 1]; +TCHAR CertDbFileName[MAX_PATH + 1]; + + +extern SERVICE_STATUS_HANDLE sshStatusHandle; + +///////////////////////////////////////////////////////////////////////// +NTSTATUS +NTDomainGet( + LPTSTR ServerName, + LPTSTR Domain + ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + None. + +--*/ + +{ + static TCHAR Serv[MAX_COMPUTERNAME_LENGTH + 3]; + UNICODE_STRING us; + NTSTATUS ret; + OBJECT_ATTRIBUTES oa; + ACCESS_MASK am; + SECURITY_QUALITY_OF_SERVICE qos; + LSA_HANDLE hLSA; + PPOLICY_PRIMARY_DOMAIN_INFO pvBuffer; + + lstrcpy(Domain, TEXT("")); + + // only need read access + am = POLICY_READ | POLICY_VIEW_LOCAL_INFORMATION; + + // set up quality of service + qos.Length = sizeof(SECURITY_QUALITY_OF_SERVICE); + qos.ImpersonationLevel = SecurityImpersonation; + qos.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING; + qos.EffectiveOnly = FALSE; + + // Macro sets everything except security field + InitializeObjectAttributes( &oa, NULL, 0L, NULL, NULL ); + oa.SecurityQualityOfService = &qos; + + if ( (ServerName == NULL) || (ServerName[0] == TEXT('\0')) ) + ret = LsaOpenPolicy(NULL, &oa, am, &hLSA); + else { + if (ServerName[0] == TEXT('\\')) + lstrcpy(Serv, ServerName); + else + wsprintf(Serv, TEXT("\\\\%s"), ServerName); + + // Set up unicode string structure + us.Length = lstrlen(Serv) * sizeof(TCHAR); + us.MaximumLength = us.Length + sizeof(TCHAR); + us.Buffer = Serv; + + ret = LsaOpenPolicy(&us, &oa, am, &hLSA); + } + + if (!ret) { + ret = LsaQueryInformationPolicy(hLSA, PolicyPrimaryDomainInformation, (PVOID *) &pvBuffer); + LsaClose(hLSA); + if ((!ret) && (pvBuffer != NULL)) { + lstrcpy(Domain, pvBuffer->Name.Buffer); + LsaFreeMemory((PVOID) pvBuffer); + } + } + + return ret; + +} // NTDomainGet + + +///////////////////////////////////////////////////////////////////////// +BOOL +NTIsPDC( + LPTSTR ServerName + ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + None. + +--*/ + +{ + static TCHAR Serv[MAX_COMPUTERNAME_LENGTH + 3]; + UNICODE_STRING us; + NTSTATUS ret; + OBJECT_ATTRIBUTES oa; + ACCESS_MASK am; + SECURITY_QUALITY_OF_SERVICE qos; + LSA_HANDLE hLSA; + PPOLICY_LSA_SERVER_ROLE_INFO pvBuffer; + BOOL IsPDC = FALSE; + + if (ServerName[0] == TEXT('\\')) + lstrcpy(Serv, ServerName); + else + wsprintf(Serv, TEXT("\\\\%s"), ServerName); + + // Set up unicode string structure + us.Length = lstrlen(Serv) * sizeof(TCHAR); + us.MaximumLength = us.Length + sizeof(TCHAR); + us.Buffer = Serv; + + // only need read access + am = POLICY_READ | POLICY_VIEW_LOCAL_INFORMATION; + + // set up quality of service + qos.Length = sizeof(SECURITY_QUALITY_OF_SERVICE); + qos.ImpersonationLevel = SecurityImpersonation; + qos.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING; + qos.EffectiveOnly = FALSE; + + // Macro sets everything except security field + InitializeObjectAttributes( &oa, NULL, 0L, NULL, NULL ); + oa.SecurityQualityOfService = &qos; + + ret = LsaOpenPolicy(&us, &oa, am, &hLSA); + + if (!ret) { + ret = LsaQueryInformationPolicy(hLSA, PolicyLsaServerRoleInformation, (PVOID *) &pvBuffer); + LsaClose(hLSA); + if ((!ret) && (pvBuffer != NULL)) { + if (pvBuffer->LsaServerRole == PolicyServerRolePrimary) + IsPDC = TRUE; + + LsaFreeMemory((PVOID) pvBuffer); + } + } + + return IsPDC; + +} // NTIsPDC + + +///////////////////////////////////////////////////////////////////////// +DWORD +LlsTimeGet( + ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + Seconds since midnight. + +--*/ + +{ + DWORD Seconds; + SYSTEMTIME SysTime; + + GetLocalTime(&SysTime); + + Seconds = (SysTime.wHour * 24 * 60) + (SysTime.wMinute * 60) + (SysTime.wSecond); + return Seconds; + +} // LlsTimeGet + + +///////////////////////////////////////////////////////////////////////// +VOID +ConfigInfoRegistryUpdate( ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ + +{ + DWORD ReplicationType, ReplicationTime; + +#if DBG + if (TraceFlags & TRACE_FUNCTION_TRACE) + dprintf(TEXT("LLS TRACE: ConfigInfoRegistryUpdate\n")); +#endif + + RtlEnterCriticalSection(&ConfigInfoLock); + + // + // Update values from Registry + // + ReplicationTime = ConfigInfo.ReplicationTime; + ReplicationType = ConfigInfo.ReplicationType; + ConfigInfoRegistryInit( &ConfigInfo.UseEnterprise, ConfigInfo.EnterpriseServer, + &ConfigInfo.ReplicationType, &ConfigInfo.ReplicationTime, + &ConfigInfo.LogLevel ); + + if ( (ConfigInfo.ReplicationTime == 0) && (LLS_REPLICATION_TYPE_TIME != ConfigInfo.ReplicationType) ) + ConfigInfo.ReplicationTime = DEFAULT_REPLICATION_TIME; + + // + // Adjust replication time if it has changed + // + if ((ReplicationTime != ConfigInfo.ReplicationTime) || (ReplicationType != ConfigInfo.ReplicationType)) + ReplicationTimeSet(); + + RtlLeaveCriticalSection(&ConfigInfoLock); + +} // ConfigInfoRegistryUpdate + + +///////////////////////////////////////////////////////////////////////// +VOID +ConfigInfoUpdate( ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ + +{ + BOOL InDomain = FALSE; + BOOL IsPDC = FALSE; + USHORT cbTotalAvail, cbBuffer; + LPBYTE pbBuffer; + NET_API_STATUS uRet; + PSERVER_INFO_101 pServer1; + DWORD ReplicationType, ReplicationTime; + TCHAR pDomain[MAX_COMPUTERNAME_LENGTH + 1]; + NT_PRODUCT_TYPE NtType; + +#if DBG + if (TraceFlags & TRACE_FUNCTION_TRACE) + dprintf(TEXT("LLS TRACE: ConfigInfoUpdate\n")); +#endif + // + // Try to get a domain + // + lstrcpy(pDomain, TEXT("")); + if ( !NTDomainGet(NULL, pDomain) ) { + InDomain = TRUE; + + // + // If we aren't a BDC/PDC then count us as a member + // + NtType = NtProductLanManNt; + RtlGetNtProductType(&NtType); + if (NtType != NtProductLanManNt) + IsPDC = FALSE; + else { + // + // Let's check if we are a PDC... + // + IsPDC = NTIsPDC(ConfigInfo.ComputerName); + } + + } else { + IsPDC = TRUE; + InDomain = FALSE; + } + + RtlEnterCriticalSection(&ConfigInfoLock); + + ConfigInfo.IsMaster = TRUE; + ConfigInfo.Replicate = FALSE; + + // + // If we are in a domain, and not the PDC then we replicate to the PDC + // + if (!IsPDC && InDomain) { + // + // Get the PDC of the domain + // + uRet = NetGetDCName(NULL, pDomain, &pbBuffer); + if (uRet == 0) { + lstrcpy(ConfigInfo.ReplicateTo, (LPWSTR) pbBuffer); + NetApiBufferFree(pbBuffer); + ConfigInfo.IsMaster = FALSE; + ConfigInfo.Replicate = TRUE; + } else { + InDomain = FALSE; + memset(ConfigInfo.ReplicateTo, 0, sizeof(ConfigInfo.ReplicateTo)); +#if DBG + dprintf(TEXT("LLS: (WARNING) NetGetDCName: 0x%lX\n"), uRet); +#endif + } + } + + // + // Update values from Registry + // + ReplicationTime = ConfigInfo.ReplicationTime; + ReplicationType = ConfigInfo.ReplicationType; + ConfigInfoRegistryInit( &ConfigInfo.UseEnterprise, ConfigInfo.EnterpriseServer, + &ConfigInfo.ReplicationType, &ConfigInfo.ReplicationTime, + &ConfigInfo.LogLevel ); + + // + // Have all registy init'd values - now need to figure out who to + // replicate to. + // + // If we are not in a domain or are a PDC then we can go to the + // Enterprise Server. + // + if (IsPDC || !InDomain) { + if (ConfigInfo.UseEnterprise) { + ConfigInfo.IsMaster = FALSE; + ConfigInfo.Replicate = TRUE; + + // + // Make sure we have an enterprise server to go to + // + if ( ConfigInfo.EnterpriseServer[0] == TEXT('\0') ) { + ConfigInfo.UseEnterprise = FALSE; + ConfigInfo.IsMaster = TRUE; + ConfigInfo.Replicate = FALSE; + } else { + // + // Base ReplicateTo on enterprise server name + // + if (ConfigInfo.EnterpriseServer[0] != TEXT('\\')) + lstrcpy(ConfigInfo.ReplicateTo, TEXT("\\\\")); + else + lstrcpy(ConfigInfo.ReplicateTo, TEXT("")); + + lstrcat(ConfigInfo.ReplicateTo, ConfigInfo.EnterpriseServer); + } + } else + ConfigInfo.IsMaster = TRUE; + } else + ConfigInfo.UseEnterprise = FALSE; + + if (ConfigInfo.IsMaster == FALSE) { + if ( (ConfigInfo.ReplicateTo == NULL) || (lstrlen(ConfigInfo.ReplicateTo) == 0) || + ( (*ConfigInfo.ReplicateTo == TEXT('\\')) && (lstrlen(ConfigInfo.ReplicateTo) < 3) )) { + ConfigInfo.IsMaster = TRUE; + ConfigInfo.Replicate = FALSE; + } + } + + // + // Adjust replication time if it has changed + // + if ((ReplicationTime != ConfigInfo.ReplicationTime) || (ReplicationType != ConfigInfo.ReplicationType)) + ReplicationTimeSet(); + + IsMaster = ConfigInfo.IsMaster; + RtlLeaveCriticalSection(&ConfigInfoLock); + +} // ConfigInfoUpdate + + +///////////////////////////////////////////////////////////////////////// +VOID +ConfigInfoInit( ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ + +{ + DWORD Size; + TCHAR DataPath[MAX_PATH + 1]; + + // + // First zero init the memory + // + memset(&ConfigInfo, 0, sizeof(CONFIG_RECORD)); + + ConfigInfo.ComputerName = LocalAlloc(LPTR, (MAX_COMPUTERNAME_LENGTH + 1) * sizeof(TCHAR)); + ConfigInfo.ReplicateTo = LocalAlloc(LPTR, (MAX_COMPUTERNAME_LENGTH + 3) * sizeof(TCHAR)); + ConfigInfo.EnterpriseServer = LocalAlloc(LPTR, (MAX_COMPUTERNAME_LENGTH + 3) * sizeof(TCHAR)); + ConfigInfo.SystemDir = LocalAlloc(LPTR, (MAX_PATH + 1) * sizeof(TCHAR)); + + if ((ConfigInfo.ComputerName == NULL) || (ConfigInfo.ReplicateTo == NULL) || (ConfigInfo.EnterpriseServer == NULL) || (ConfigInfo.SystemDir == NULL) ) { + ASSERT(FALSE); + } + + ConfigInfo.Version = INTERNAL_VERSION; + GetLocalTime(&ConfigInfo.Started); + + // + // LastReplicated is just for display, LlsReplTime is what is used to + // Calculate it. + GetLocalTime(&ConfigInfo.LastReplicated); + ConfigInfo.LastReplicatedSeconds = DateSystemGet(); + + GetSystemDirectory(ConfigInfo.SystemDir, MAX_PATH); + lstrcat(ConfigInfo.SystemDir, TEXT("\\")); + + ConfigInfo.IsMaster = TRUE; + + ConfigInfo.Replicate = FALSE; + ConfigInfo.IsReplicating = FALSE; + + ConfigInfo.ReplicationType = REPLICATE_DELTA; + ConfigInfo.ReplicationTime = DEFAULT_REPLICATION_TIME; + + Size = MAX_COMPUTERNAME_LENGTH + 1; + GetComputerName(ConfigInfo.ComputerName, &Size); + NTDomainGet( ConfigInfo.ComputerName, MyDomain); + lstrcat(MyDomain, TEXT("\\")); + MyDomainSize = (lstrlen(MyDomain) + 1) * sizeof(TCHAR); + + RtlInitializeCriticalSection(&ConfigInfoLock); + + ConfigInfoUpdate(); + + // + // Create File paths + // + lstrcpy(MappingFileName, ConfigInfo.SystemDir); + lstrcat(MappingFileName, TEXT(LLS_FILE_SUBDIR)); + lstrcat(MappingFileName, TEXT("\\")); + lstrcat(MappingFileName, TEXT(MAP_FILE_NAME)); + + lstrcpy(UserFileName, ConfigInfo.SystemDir); + lstrcat(UserFileName, TEXT(LLS_FILE_SUBDIR)); + lstrcat(UserFileName, TEXT("\\")); + lstrcat(UserFileName, TEXT(USER_FILE_NAME)); + + lstrcpy(CertDbFileName, ConfigInfo.SystemDir); + lstrcat(CertDbFileName, TEXT(LLS_FILE_SUBDIR)); + lstrcat(CertDbFileName, TEXT("\\")); + lstrcat(CertDbFileName, TEXT(CERT_DB_FILE_NAME)); + + lstrcpy(LicenseFileName, ConfigInfo.SystemDir); + lstrcat(LicenseFileName, TEXT(LICENSE_FILE_NAME)); + + // + // Make sure our directory is there. + // + lstrcpy(DataPath, ConfigInfo.SystemDir); + lstrcat(DataPath, TEXT(LLS_FILE_SUBDIR)); + CreateDirectory(DataPath, NULL); + +} // ConfigInfoInit + + +///////////////////////////////////////////////////////////////////////// +DWORD WINAPI +LLSTopLevelExceptionHandler( + struct _EXCEPTION_POINTERS *ExceptionInfo + ) + +/*++ + +Routine Description: + + The Top Level exception filter for LLSMain.exe. + + This ensures the entire process will be cleaned up if any of + the threads fail. Since LLSMain.exe is a distributed application, + it is better to fail the entire process than allow random threads + to continue executing. + +Arguments: + + ExceptionInfo - Identifies the exception that occurred. + + +Return Values: + + EXCEPTION_EXECUTE_HANDLER - Terminate the process. + + EXCEPTION_CONTINUE_SEARCH - Continue processing as though this filter + was never called. + + +--*/ +{ + HANDLE hModule; + + + // + // Raise an alert + // + + hModule = LoadLibraryA("netapi32"); + + if ( hModule != NULL ) { + NET_API_STATUS (NET_API_FUNCTION *NetAlertRaiseExFunction) + (LPTSTR, LPVOID, DWORD, LPTSTR); + + + NetAlertRaiseExFunction = + (NET_API_STATUS (NET_API_FUNCTION *) (LPTSTR, LPVOID, DWORD, LPTSTR)) + GetProcAddress(hModule, "NetAlertRaiseEx"); + + if ( NetAlertRaiseExFunction != NULL ) { + NTSTATUS Status; + UNICODE_STRING Strings; + + char message[ALERTSZ + sizeof(ADMIN_OTHER_INFO)]; + PADMIN_OTHER_INFO admin = (PADMIN_OTHER_INFO) message; + + // + // Build the variable data + // + + admin->alrtad_errcode = ALERT_UnhandledException; + admin->alrtad_numstrings = 0; + + Strings.Buffer = (LPWSTR) ALERT_VAR_DATA(admin); + Strings.Length = 0; + Strings.MaximumLength = ALERTSZ; + + Status = RtlIntegerToUnicodeString( + (ULONG)ExceptionInfo->ExceptionRecord->ExceptionCode, + 16, + &Strings ); + + if ( NT_SUCCESS(Status) ) { + if ( Strings.Length + sizeof(WCHAR) >= Strings.MaximumLength) { + Status = STATUS_BUFFER_TOO_SMALL; + } else { + admin->alrtad_numstrings++; + *(Strings.Buffer+(Strings.Length/sizeof(WCHAR))) = L'\0'; + Strings.Length += sizeof(WCHAR); + + Status = RtlAppendUnicodeToString( &Strings, L"LLS" ); + } + + } + + if ( NT_SUCCESS(Status) ) { + if ( Strings.Length + sizeof(WCHAR) >= Strings.MaximumLength) { + Status = STATUS_BUFFER_TOO_SMALL; + } else { + admin->alrtad_numstrings++; + *(Strings.Buffer+(Strings.Length/sizeof(WCHAR))) = L'\0'; + Strings.Buffer += (Strings.Length/sizeof(WCHAR)) + 1; + Strings.MaximumLength -= Strings.Length + sizeof(WCHAR); + Strings.Length = 0; + + Status = RtlIntegerToUnicodeString( + (ULONG)ExceptionInfo->ExceptionRecord->ExceptionAddress, + 16, + &Strings ); + } + + } + + if ( NT_SUCCESS(Status) ) { + if ( Strings.Length + sizeof(WCHAR) >= Strings.MaximumLength) { + Status = STATUS_BUFFER_TOO_SMALL; + } else { + admin->alrtad_numstrings++; + *(Strings.Buffer+(Strings.Length/sizeof(WCHAR))) = L'\0'; + Strings.Buffer += (Strings.Length/sizeof(WCHAR)) + 1; + + (VOID) (*NetAlertRaiseExFunction)( + ALERT_ADMIN_EVENT, + message, + (DWORD)((PCHAR)Strings.Buffer - + (PCHAR)message), + L"LLS" ); + } + + } + + + } + + (VOID) FreeLibrary( hModule ); + } + + + // + // Just continue processing the exception. + // + + return EXCEPTION_CONTINUE_SEARCH; + +} // LLSTopLevelExceptionHandler + + +///////////////////////////////////////////////////////////////////////// +VOID +ServiceStart ( + DWORD dwArgc, + LPTSTR *lpszArgv + ) +/*++ + +Routine Description: + + The code that starts everything, is really the main(). + +Arguments: + + None. + +Return Values: + + None. + +--*/ +{ + DWORD dwWait; + NTSTATUS Status = STATUS_SUCCESS; + BOOLEAN EnableAlignmentFaults = TRUE; + KPRIORITY BasePriority; + + /////////////////////////////////////////////////// + // + // Service initialization + // + + // + // Report the status to the service control manager. + // + if (!ReportStatusToSCMgr( SERVICE_START_PENDING, NO_ERROR, 3000)) // wait hint + goto Cleanup; + + // + // Create the event object. The control handler function signals + // this event when it receives the "stop" control code. + // + hServerStopEvent = CreateEvent( + NULL, // no security attributes + TRUE, // manual reset event + FALSE, // not-signalled + NULL); // no name + + if ( hServerStopEvent == NULL) + goto Cleanup; + + // + // Report the status to the service control manager. + // + if (!ReportStatusToSCMgr( SERVICE_START_PENDING, NO_ERROR, 3000)) // wait hint + goto Cleanup; + + + // + // Define a top-level exception handler for the entire process. + // + + (VOID) SetErrorMode( SEM_FAILCRITICALERRORS ); + + (VOID) SetUnhandledExceptionFilter( &LLSTopLevelExceptionHandler ); + + // + // Turn on alignment fault fixups. This is necessary because + // several structures stored in the registry have qword aligned + // fields. They are nicely aligned in our structures, but they + // end up being forced out of alignment when being stored because + // the registry api require data to be passed following a wierd + // length header. + // + + Status = NtSetInformationProcess( + NtCurrentProcess(), + ProcessEnableAlignmentFaultFixup, + (PVOID) &EnableAlignmentFaults, + sizeof(BOOLEAN) + ); + ASSERT(NT_SUCCESS(Status)); + + // + // Report the status to the service control manager. + // + if (!ReportStatusToSCMgr( SERVICE_START_PENDING, NO_ERROR, 3000)) // wait hint + goto Cleanup; + + // + // Run the LLS in the foreground. + // + // Several processes which depend on the LLS (like the lanman server) + // run in the foreground. If we don't run in the foreground, they'll + // starve waiting for us. + // + + BasePriority = FOREGROUND_BASE_PRIORITY; + + Status = NtSetInformationProcess( + NtCurrentProcess(), + ProcessBasePriority, + &BasePriority, + sizeof(BasePriority) + ); + + // + // Report the status to the service control manager. + // + if (!ReportStatusToSCMgr( SERVICE_START_PENDING, NO_ERROR, 3000)) // wait hint + goto Cleanup; + + // Initialize the Registry values... + RegistryInit(); + + // + // Report the status to the service control manager. + // + if (!ReportStatusToSCMgr( SERVICE_START_PENDING, NO_ERROR, 3000)) // wait hint + goto Cleanup; + + // Initialize the Registry values... + ConfigInfoInit(); + + // + // Report the status to the service control manager. + // + if (!ReportStatusToSCMgr( SERVICE_START_PENDING, NO_ERROR, 3000)) // wait hint + goto Cleanup; + + // Initialize the Service Table + LicenseListInit(); + + // + // Report the status to the service control manager. + // + if (!ReportStatusToSCMgr( SERVICE_START_PENDING, NO_ERROR, 3000)) // wait hint + goto Cleanup; + + // Initialize the Service Table + MasterServiceListInit(); + + // + // Report the status to the service control manager. + // + if (!ReportStatusToSCMgr( SERVICE_START_PENDING, NO_ERROR, 3000)) // wait hint + goto Cleanup; + + // Initialize the Service Table + LocalServiceListInit(); + + // + // Report the status to the service control manager. + // + if (!ReportStatusToSCMgr( SERVICE_START_PENDING, NO_ERROR, 3000)) // wait hint + goto Cleanup; + + // Initialize the Service Table + ServiceListInit(); + + // + // Report the status to the service control manager. + // + if (!ReportStatusToSCMgr( SERVICE_START_PENDING, NO_ERROR, 3000)) // wait hint + goto Cleanup; + + // Initialize the Service Table + MappingListInit(); + + // + // Report the status to the service control manager. + // + if (!ReportStatusToSCMgr( SERVICE_START_PENDING, NO_ERROR, 3000)) // wait hint + goto Cleanup; + + // Initialize the Per-Seat Table + UserListInit(); + + // + // Report the status to the service control manager. + // + if (!ReportStatusToSCMgr( SERVICE_START_PENDING, NO_ERROR, 3000)) // wait hint + goto Cleanup; + + // Initialize the Service Table + ServerListInit(); + + // + // Report the status to the service control manager. + // + if (!ReportStatusToSCMgr( SERVICE_START_PENDING, NO_ERROR, 3000)) // wait hint + goto Cleanup; + + // Initialize the Certificate Database + CertDbInit(); + + // + // Report the status to the service control manager - need a bit longer + // to read in all the data files. + // + if (!ReportStatusToSCMgr( SERVICE_START_PENDING, NO_ERROR, 15000)) // wait hint + goto Cleanup; + + // Load data files + LoadAll(); + + // + // Report the status to the service control manager. + // + if (!ReportStatusToSCMgr( SERVICE_START_PENDING, NO_ERROR, 3000)) // wait hint + goto Cleanup; + + // Initialize RPC Stuff... + LLSRpcInit(); + + // + // Report the status to the service control manager. + // + if (!ReportStatusToSCMgr( SERVICE_START_PENDING, NO_ERROR, 3000)) // wait hint + goto Cleanup; + + // Initialize Replication... + ReplicationInit(); + + // + // Report the status to the service control manager. + // + if (!ReportStatusToSCMgr( SERVICE_START_PENDING, NO_ERROR, 3000)) // wait hint + goto Cleanup; + + // Initialize scavenger thread... + ScavengerInit(); + + // + // Report the status to the service control manager. + // + if (!ReportStatusToSCMgr( SERVICE_START_PENDING, NO_ERROR, 3000)) // wait hint + goto Cleanup; + + // Initialize RegistryMonitor thread... + RegistryStartMonitor(); + + // + // End of initialization + // + //////////////////////////////////////////////////////// + + // + // Tell SCM we are up and running! + // + if (!ReportStatusToSCMgr( SERVICE_RUNNING, NO_ERROR, 0)) // wait hint + goto Cleanup; + + //////////////////////////////////////////////////////// + // + // Service is now running, perform work until shutdown + // + dwWait = WaitForSingleObject(hServerStopEvent, INFINITE); + +Cleanup: + + if (hServerStopEvent) + CloseHandle(hServerStopEvent); + + if (sshStatusHandle) + ReportStatusToSCMgr( SERVICE_STOPPED, NO_ERROR, 0); + +} // ServiceStart + + +///////////////////////////////////////////////////////////////////////// +VOID ServiceStop() +/*++ + +Routine Description: + + Stops the service. + + If a ServiceStop procedure is going to take longer than 3 seconds to + execute, it should spawn a thread to execute the stop code, and return. + Otherwise, the ServiceControlManager will believe that the service has + stopped responding. + +Arguments: + + None. + +Return Values: + + None. + +--*/ +{ + if ( hServerStopEvent ) + SetEvent(hServerStopEvent); +} // ServiceStop + + +#if DBG +///////////////////////////////////////////////////////////////////////// +VOID +ConfigInfoDebugDump( ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ + +{ + RtlEnterCriticalSection(&ConfigInfoLock); + + dprintf(TEXT("License Logging Service - Version: 0x%lX\n"), ConfigInfo.Version); + dprintf(TEXT(" Started: %u-%u-%u @ %u:%u:%u\n"), + (UINT) ConfigInfo.Started.wDay, + (UINT) ConfigInfo.Started.wMonth, + (UINT) ConfigInfo.Started.wYear, + (UINT) ConfigInfo.Started.wHour, + (UINT) ConfigInfo.Started.wMinute, + (UINT) ConfigInfo.Started.wSecond ); + + dprintf(TEXT(" Replication\n")); + dprintf(TEXT(" +--------------+\n")); + if (ConfigInfo.IsMaster) + dprintf(TEXT(" Master Server\n")); + else + dprintf(TEXT(" NOT Master Server\n")); + + if (ConfigInfo.Replicate) + dprintf(TEXT(" Replicates\n")); + else + dprintf(TEXT(" Does not Replicate\n")); + + if (ConfigInfo.IsReplicating) + dprintf(TEXT(" Currently Replicating\n")); + else + dprintf(TEXT(" NOT Currently Replicating\n")); + + dprintf(TEXT(" Replicates To: %s\n"), ConfigInfo.ReplicateTo); + dprintf(TEXT(" Enterprise Server: %s\n"), ConfigInfo.EnterpriseServer); + + if (ConfigInfo.ReplicationType == REPLICATE_DELTA) + dprintf(TEXT(" Replicate Every: %lu Seconds\n"), ConfigInfo.ReplicationTime ); + else + dprintf(TEXT(" Replicate @: %lu\n"), ConfigInfo.ReplicationTime ); + + dprintf(TEXT("\n Last Replicated: %u-%u-%u @ %u:%u:%u\n\n"), + (UINT) ConfigInfo.LastReplicated.wDay, + (UINT) ConfigInfo.LastReplicated.wMonth, + (UINT) ConfigInfo.LastReplicated.wYear, + (UINT) ConfigInfo.LastReplicated.wHour, + (UINT) ConfigInfo.LastReplicated.wMinute, + (UINT) ConfigInfo.LastReplicated.wSecond ); + + dprintf(TEXT(" Number Servers Currently Replicating: %lu\n"), ConfigInfo.NumReplicating); + + dprintf(TEXT(" Current Backoff Time Delta: %lu\n"), ConfigInfo.BackoffTime); + + dprintf(TEXT(" Current Replication Speed: %lu\n"), ConfigInfo.ReplicationSpeed); + + RtlLeaveCriticalSection(&ConfigInfoLock); + +} // ConfigInfoDebugDump +#endif diff --git a/private/net/svcdlls/lls/server/llssrv.h b/private/net/svcdlls/lls/server/llssrv.h new file mode 100644 index 000000000..1601a4f0e --- /dev/null +++ b/private/net/svcdlls/lls/server/llssrv.h @@ -0,0 +1,109 @@ +/*++ + +Copyright (c) 1994 Microsoft Corporation + +Module Name: + + LlsSrv.h + +Abstract: + + +Author: + + Arthur Hanson (arth) Dec 07, 1994 + +Environment: + +Revision History: + + Jeff Parham (jeffparh) 05-Dec-1995 + o Added certificate database support. + +--*/ + +#ifndef _LLS_LLSSRV_H +#define _LLS_LLSSRV_H + + +#ifdef __cplusplus +extern "C" { +#endif + +#define MAP_FILE_NAME "LlsMap.LLS" +#define USER_FILE_NAME "LlsUser.LLS" +#define LICENSE_FILE_NAME "CPL.CFG" +#define CERT_DB_FILE_NAME "LlsCert.LLS" + +#define LLS_FILE_SUBDIR "LLS" + + +#define REPLICATE_DELTA 0 +#define REPLICATE_AT 1 + +#define MAX_USERNAME_LENGTH 256 +#define MAX_DOMAINNAME_LENGTH MAX_COMPUTERNAME_LENGTH + + +///////////////////////////////////////////////////////////////////////// +typedef struct _CONFIG_RECORD { + SYSTEMTIME Started; + DWORD Version; + LPTSTR SystemDir; + + // + // Replication Info + // + LPTSTR ComputerName; + LPTSTR ReplicateTo; + LPTSTR EnterpriseServer; + DWORD EnterpriseServerDate; + DWORD LogLevel; + + // When to replicate + ULONG ReplicationType; + ULONG ReplicationTime; + DWORD UseEnterprise; + + DWORD LastReplicatedSeconds; + DWORD NextReplication; + SYSTEMTIME LastReplicated; + + ULONG NumReplicating; // Number of machines currently replicating here + ULONG BackoffTime; + ULONG ReplicationSpeed; + + BOOL IsMaster; // TRUE if is a Master Server (top of repl tree). + BOOL Replicate; // Whether this server replicates + BOOL IsReplicating; // TRUE if currently replicating +} CONFIG_RECORD, *PCONFIG_RECORD; + +extern CONFIG_RECORD ConfigInfo; +extern RTL_CRITICAL_SECTION ConfigInfoLock; + +extern TCHAR MyDomain[]; +extern ULONG MyDomainSize; + +extern BOOL IsMaster; + +extern TCHAR MappingFileName[]; +extern TCHAR UserFileName[]; +extern TCHAR LicenseFileName[]; +extern TCHAR CertDbFileName[]; + + +DWORD LlsTimeGet(); +VOID ConfigInfoUpdate(); +VOID ConfigInfoRegistryUpdate( ); + +///////////////////////////////////////////////////////////////////////// +#if DBG + +VOID ConfigInfoDebugDump(); +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/private/net/svcdlls/lls/server/llssrv.rc b/private/net/svcdlls/lls/server/llssrv.rc new file mode 100644 index 000000000..8fd373a89 --- /dev/null +++ b/private/net/svcdlls/lls/server/llssrv.rc @@ -0,0 +1,12 @@ +#include <windows.h> +#include <ntverp.h> + +#define VER_FILETYPE VFT_APP +#define VER_FILESUBTYPE VFT2_UNKNOWN +#define VER_FILEDESCRIPTION_STR "Microsoft\256 License Server" + +#define VER_INTERNALNAME_STR "llssrv.exe" +#define VER_ORIGINALFILENAME_STR "llssrv.exe" + +#include <common.ver> + diff --git a/private/net/svcdlls/lls/server/llsutil.c b/private/net/svcdlls/lls/server/llsutil.c new file mode 100644 index 000000000..6b52e6791 --- /dev/null +++ b/private/net/svcdlls/lls/server/llsutil.c @@ -0,0 +1,982 @@ +/*++ + +Copyright (c) 1995 Microsoft Corporation + +Module Name: + + LlsUtil.c + +Abstract: + + +Author: + + Arthur Hanson (arth) Dec 07, 1994 + +Environment: + +Revision History: + + Jeff Parham (jeffparh) 12-Jan-1996 + o Added WinNtBuildNumberGet() to ascertain the Windows NT build number + running on a given machine. + o Enhanced output of TimeToString(). + +--*/ + +#include <nt.h> +#include <ntrtl.h> +#include <nturtl.h> +#include <ntlsa.h> + +#include <windows.h> +#include <stdlib.h> +#include <crypt.h> +#include <wchar.h> + +#include "debug.h" +#include "llssrv.h" + + +char HeaderString[] = "License Logging System Data File\x01A"; +#define HEADER_SIZE 34 + +typedef struct _LLS_FILE_HEADER { + char Header[HEADER_SIZE]; + DWORD Version; + DWORD DataSize; +} LLS_FILE_HEADER, *PLLS_FILE_HEADER; + + + +///////////////////////////////////////////////////////////////////////// +NTSTATUS +EBlock( + PVOID Data, + ULONG DataSize + ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ + +{ + NTSTATUS Status = STATUS_SUCCESS; + DATA_KEY PublicKey; + CRYPT_BUFFER CryptBuffer; + + // + // Init our public key + // + PublicKey.Length = 4; + PublicKey.MaximumLength = 4; + PublicKey.Buffer = LocalAlloc(LPTR, 4); + + if (PublicKey.Buffer != NULL) { + ((char *) (PublicKey.Buffer))[0] = '7'; + ((char *) (PublicKey.Buffer))[1] = '7'; + ((char *) (PublicKey.Buffer))[2] = '7'; + ((char *) (PublicKey.Buffer))[3] = '7'; + + CryptBuffer.Length = DataSize; + CryptBuffer.MaximumLength = DataSize; + CryptBuffer.Buffer = (PVOID) Data; + Status = RtlEncryptData2(&CryptBuffer, &PublicKey); + + LocalFree(PublicKey.Buffer); + } else + Status = STATUS_NO_MEMORY; + + return Status; +} // EBlock + + +///////////////////////////////////////////////////////////////////////// +NTSTATUS +DeBlock( + PVOID Data, + ULONG DataSize + ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ + +{ + NTSTATUS Status = STATUS_SUCCESS; + DATA_KEY PublicKey; + CRYPT_BUFFER CryptBuffer; + + // + // Init our public key + // + PublicKey.Length = 4; + PublicKey.MaximumLength = 4; + PublicKey.Buffer = LocalAlloc(LPTR, 4); + if (PublicKey.Buffer != NULL) { + ((char *) (PublicKey.Buffer))[0] = '7'; + ((char *) (PublicKey.Buffer))[1] = '7'; + ((char *) (PublicKey.Buffer))[2] = '7'; + ((char *) (PublicKey.Buffer))[3] = '7'; + + CryptBuffer.Length = DataSize; + CryptBuffer.MaximumLength = DataSize; + CryptBuffer.Buffer = (PVOID) Data; + Status = RtlDecryptData2(&CryptBuffer, &PublicKey); + + LocalFree(PublicKey.Buffer); + } else + Status = STATUS_NO_MEMORY; + + return Status; +} // DeBlock + + +///////////////////////////////////////////////////////////////////////// +BOOL +FileExists( + LPTSTR FileName + ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ + +{ + return (BOOL) RtlDoesFileExists_U(FileName); + +} // FileExists + + +///////////////////////////////////////////////////////////////////////// +VOID +lsplitpath( + const TCHAR *path, + TCHAR *drive, + TCHAR *dir, + TCHAR *fname, + TCHAR *ext + ) + +/*++ + +Routine Description: + Splits a path name into its individual components + + Took the _splitpath and _makepath routines and converted them to + be NT (long file name) and Unicode friendly. + +Arguments: + Entry: + path - pointer to path name to be parsed + drive - pointer to buffer for drive component, if any + dir - pointer to buffer for subdirectory component, if any + fname - pointer to buffer for file base name component, if any + ext - pointer to buffer for file name extension component, if any + + Exit: + drive - pointer to drive string. Includes ':' if a drive was given. + dir - pointer to subdirectory string. Includes leading and + trailing '/' or '\', if any. + fname - pointer to file base name + ext - pointer to file extension, if any. Includes leading '.'. + +Return Value: + + +--*/ + +{ + TCHAR *p; + TCHAR *last_slash = NULL, *dot = NULL; + unsigned len; + + // init these so we don't exit with bogus values + drive[0] = TEXT('\0'); + dir[0] = TEXT('\0'); + fname[0] = TEXT('\0'); + ext[0] = TEXT('\0'); + + if (path[0] == TEXT('\0')) + return; + + /*+---------------------------------------------------------------------+ + | Assume that the path argument has the following form, where any or | + | all of the components may be missing. | + | | + | <drive><dir><fname><ext> | + | | + | drive: | + | 0 to MAX_DRIVE-1 characters, the last of which, if any, is a | + | ':' or a '\' in the case of a UNC path. | + | dir: | + | 0 to _MAX_DIR-1 characters in the form of an absolute path | + | (leading '/' or '\') or relative path, the last of which, if | + | any, must be a '/' or '\'. E.g - | + | | + | absolute path: | + | \top\next\last\ ; or | + | /top/next/last/ | + | relative path: | + | top\next\last\ ; or | + | top/next/last/ | + | Mixed use of '/' and '\' within a path is also tolerated | + | fname: | + | 0 to _MAX_FNAME-1 characters not including the '.' character | + | ext: | + | 0 to _MAX_EXT-1 characters where, if any, the first must be a | + | '.' | + +---------------------------------------------------------------------+*/ + + // extract drive letter and :, if any + if ( path[0] && (path[1] == TEXT(':')) ) { + if (drive) { + drive[0] = path[0]; + drive[1] = path[1]; + drive[2] = TEXT('\0'); + } + path += 2; + } + + // if no drive then check for UNC pathname + if (drive[0] == TEXT('\0')) + if ((path[0] == TEXT('\\')) && (path[1] == TEXT('\\'))) { + // got a UNC path so put server-sharename into drive + drive[0] = path[0]; + drive[1] = path[1]; + path += 2; + + p = &drive[2]; + while ((*path != TEXT('\0')) && (*path != TEXT('\\'))) + *p++ = *path++; + + if (*path == TEXT('\0')) + return; + + // now sitting at the share - copy this as well (copy slash first) + *p++ = *path++; + while ((*path != TEXT('\0')) && (*path != TEXT('\\'))) + *p++ = *path++; + + // tack on terminating NULL + *p = TEXT('\0'); + } + + /*+---------------------------------------------------------------------+ + | extract path string, if any. Path now points to the first character| + | of the path, if any, or the filename or extension, if no path was | + | specified. Scan ahead for the last occurence, if any, of a '/' or | + | '\' path separator character. If none is found, there is no path. | + | We will also note the last '.' character found, if any, to aid in | + | handling the extension. | + +---------------------------------------------------------------------+*/ + + for (last_slash = NULL, p = (TCHAR *)path; *p; p++) { + if (*p == TEXT('/') || *p == TEXT('\\')) + // point to one beyond for later copy + last_slash = p + 1; + else if (*p == TEXT('.')) + dot = p; + } + + if (last_slash) { + + // found a path - copy up through last_slash or max. characters allowed, + // whichever is smaller + if (dir) { + len = __min((last_slash - path), (_MAX_DIR - 1)); + lstrcpyn(dir, path, len + 1); + dir[len] = TEXT('\0'); + } + path = last_slash; + } + + /*+---------------------------------------------------------------------+ + | extract file name and extension, if any. Path now points to the | + | first character of the file name, if any, or the extension if no | + | file name was given. Dot points to the '.' beginning the extension,| + | if any. | + +---------------------------------------------------------------------+*/ + + if (dot && (dot >= path)) { + // found the marker for an extension - copy the file name up to the + // '.'. + if (fname) { + len = __min((dot - path), (_MAX_FNAME - 1)); + lstrcpyn(fname, path, len + 1); + *(fname + len) = TEXT('\0'); + } + + // now we can get the extension - remember that p still points to the + // terminating nul character of path. + if (ext) { + len = __min((p - dot), (_MAX_EXT - 1)); + lstrcpyn(ext, dot, len + 1); + ext[len] = TEXT('\0'); + } + } + else { + // found no extension, give empty extension and copy rest of string + // into fname. + if (fname) { + len = __min((p - path), (_MAX_FNAME - 1)); + lstrcpyn(fname, path, len + 1); + fname[len] = TEXT('\0'); + } + if (ext) { + *ext = TEXT('\0'); + } + } + +} // lsplitpath + + +///////////////////////////////////////////////////////////////////////// +VOID +lmakepath( + TCHAR *path, + const TCHAR *drive, + const TCHAR *dir, + const TCHAR *fname, + const TCHAR *ext + ) + +/*++ + +Routine Description: + Create a path name from its individual components. + +Arguments: + Entry: + char *path - pointer to buffer for constructed path + char *drive - pointer to drive component, may or may not contain + trailing ':' + char *dir - pointer to subdirectory component, may or may not include + leading and/or trailing '/' or '\' characters + char *fname - pointer to file base name component + char *ext - pointer to extension component, may or may not contain + a leading '.'. + + Exit: + path - pointer to constructed path name + +Return Value: + + +--*/ + +{ + const TCHAR *p; + + /*+---------------------------------------------------------------------+ + | we assume that the arguments are in the following form (although we | + | do not diagnose invalid arguments or illegal filenames (such as | + | names longer than 8.3 or with illegal characters in them) | + | | + | drive: | + | A or A: | + | dir: | + | \top\next\last\ ; or | + | /top/next/last/ ; or | + | | + | either of the above forms with either/both the leading and | + | trailing / or \ removed. Mixed use of '/' and '\' is also | + | tolerated | + | fname: | + | any valid file name | + | ext: | + | any valid extension (none if empty or null ) | + +---------------------------------------------------------------------+*/ + + // copy drive + if (drive && *drive) + while (*drive) + *path++ = *drive++; + + // copy dir + if ((p = dir) && *p) { + do { + *path++ = *p++; + } + while (*p); + if ((*(p-1) != TEXT('/')) && (*(p-1) != TEXT('\\'))) { + *path++ = TEXT('\\'); + } + } + + // copy fname + if (p = fname) { + while (*p) { + *path++ = *p++; + } + } + + // copy ext, including 0-terminator - check to see if a '.' needs to be + // inserted. + if (p = ext) { + if (*p && *p != TEXT('.')) { + *path++ = TEXT('.'); + } + while (*path++ = *p++) + ; + } + else { + // better add the 0-terminator + *path = TEXT('\0'); + } + +} // lmakepath + + +///////////////////////////////////////////////////////////////////////// +VOID +FileBackupCreate( + LPTSTR Path + ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ + +{ + HANDLE hFile = NULL; + DWORD dwFileNumber = 0; + TCHAR Drive[_MAX_DRIVE + 1]; + TCHAR Dir[_MAX_DIR + 1]; + TCHAR FileName[_MAX_FNAME + 1]; + TCHAR Ext[_MAX_EXT + 1]; + TCHAR NewExt[_MAX_EXT + 1]; + TCHAR NewPath[MAX_PATH + 1]; + + // + // Make sure file exists + // + if (!FileExists(FileName)) + return; + + // + // Split name into constituent parts... + // + lsplitpath(Path, Drive, Dir, FileName, Ext); + + // Find next backup number... + // Files are backed up as .xxx where xxx is a number in the form .001, + // the first backup is stored as .001, second as .002, etc... + do { + // + // Create new file name with backup extension + // + dwFileNumber++; + wsprintf(NewExt, TEXT("%03u"), dwFileNumber); + lmakepath(NewPath, Drive, Dir, FileName, NewExt); + + } while ( FileExists(NewPath) ); + + MoveFile( Path, NewPath ); + +} // FileBackupCreate + + +///////////////////////////////////////////////////////////////////////// +HANDLE +LlsFileInit( + LPTSTR FileName, + DWORD Version, + DWORD DataSize + ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ + +{ + HANDLE hFile = NULL; + LLS_FILE_HEADER Header; + DWORD BytesWritten; + +#ifdef DEBUG + if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_DATABASE)) + dprintf(TEXT("LLS TRACE: LlsFileInit\n")); +#endif + + if (FileName == NULL) + return NULL; + + strcpy(Header.Header, HeaderString); + Header.Version = Version; + Header.DataSize = DataSize; + + SetFileAttributes(FileName, FILE_ATTRIBUTE_NORMAL); + hFile = CreateFile(FileName, GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + if (hFile != INVALID_HANDLE_VALUE) { + if (!WriteFile(hFile, &Header, sizeof(LLS_FILE_HEADER), &BytesWritten, NULL)) { + CloseHandle(hFile); + hFile = NULL; + } + } + + return hFile; +} // LlsFileInit + + +///////////////////////////////////////////////////////////////////////// +HANDLE +LlsFileCheck( + LPTSTR FileName, + LPDWORD Version, + LPDWORD DataSize + ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ + +{ + BOOL FileOK = FALSE; + HANDLE hFile = NULL; + LLS_FILE_HEADER Header; + DWORD FileSize; + DWORD BytesRead; + +#ifdef DEBUG + if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_DATABASE)) + dprintf(TEXT("LLS TRACE: LlsFileCheck\n")); +#endif + + if (FileName == NULL) + return NULL; + + // + // We are assuming the file exists + // + SetFileAttributes(FileName, FILE_ATTRIBUTE_NORMAL); + hFile = CreateFile(FileName, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (hFile != INVALID_HANDLE_VALUE) { + FileSize = GetFileSize(hFile, NULL); + + // + // Make sure there is enough data there to read + // + if (FileSize > (sizeof(LLS_FILE_HEADER) + 1)) { + if (ReadFile(hFile, &Header, sizeof(LLS_FILE_HEADER), &BytesRead, NULL)) { + if ( !_strcmpi(Header.Header, HeaderString) ) { + // + // Data checks out - so return datalength + // + *Version = Header.Version; + *DataSize = Header.DataSize; + FileOK = TRUE; + } + } + } + + // + // If we opened the file and something was wrong - close it. + // + if (!FileOK) { + CloseHandle(hFile); + hFile = NULL; + } + } + + return hFile; + +} // LlsFileCheck + + +///////////////////////////////////////////////////////////////////////// +DWORD +DateSystemGet( + ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + Seconds since midnight. + +--*/ + +{ + DWORD Seconds; + LARGE_INTEGER SysTime; + + NtQuerySystemTime(&SysTime); + RtlTimeToSecondsSince1980(&SysTime, &Seconds); + return Seconds; + +} // DateSystemGet + + +///////////////////////////////////////////////////////////////////////// +DWORD +DateLocalGet( + ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + Seconds since midnight. + +--*/ + +{ + DWORD Seconds; + LARGE_INTEGER SysTime, LocalTime; + + NtQuerySystemTime(&SysTime); + RtlSystemTimeToLocalTime(&SysTime, &LocalTime); + RtlTimeToSecondsSince1980(&LocalTime, &Seconds); + return Seconds; + +} // DateLocalGet + + +///////////////////////////////////////////////////////////////////////// +DWORD +InAWorkgroup( + VOID + ) +/*++ + +Routine Description: + + This function determines whether we are a member of a domain, or of + a workgroup. First it checks to make sure we're running on a Windows NT + system (otherwise we're obviously in a domain) and if so, queries LSA + to get the Primary domain SID, if this is NULL, we're in a workgroup. + + If we fail for some random unexpected reason, we'll pretend we're in a + workgroup (it's more restrictive). + +Arguments: + None + +Return Value: + + TRUE - We're in a workgroup + FALSE - We're in a domain + +--*/ +{ + NT_PRODUCT_TYPE ProductType; + OBJECT_ATTRIBUTES ObjectAttributes; + LSA_HANDLE Handle; + NTSTATUS Status; + PPOLICY_PRIMARY_DOMAIN_INFO PolicyPrimaryDomainInfo = NULL; + DWORD Result = FALSE; + + + Status = RtlGetNtProductType(&ProductType); + + if (!NT_SUCCESS(Status)) { +#if DBG + dprintf(TEXT("ERROR LLS Could not get Product type\n")); +#endif + return TRUE; + } + + if (ProductType == NtProductLanManNt) { + return(FALSE); + } + + InitializeObjectAttributes(&ObjectAttributes, NULL, 0, 0, NULL); + + Status = LsaOpenPolicy(NULL, + &ObjectAttributes, + POLICY_VIEW_LOCAL_INFORMATION, + &Handle); + + if (!NT_SUCCESS(Status)) { +#if DBG + dprintf(TEXT("ERROR LLS: Could not open LSA Policy Database\n")); +#endif + return TRUE; + } + + Status = LsaQueryInformationPolicy(Handle, PolicyPrimaryDomainInformation, + (PVOID *) &PolicyPrimaryDomainInfo); + + if (NT_SUCCESS(Status)) { + + if (PolicyPrimaryDomainInfo->Sid == NULL) { + Result = TRUE; + } + else { + Result = FALSE; + } + } + + if (PolicyPrimaryDomainInfo) { + LsaFreeMemory((PVOID)PolicyPrimaryDomainInfo); + } + + LsaClose(Handle); + + return(Result); +} // InAWorkgroup + + +///////////////////////////////////////////////////////////////////////// +VOID +LogEvent( + DWORD MessageId, + DWORD NumberOfSubStrings, + LPWSTR *SubStrings, + DWORD ErrorCode + ) +{ + + HANDLE LogHandle; + WORD wEventType; + + LogHandle = RegisterEventSourceW ( + NULL, + TEXT("LicenseService") + ); + + if (LogHandle == NULL) { +#if DBG + dprintf(TEXT("LLS RegisterEventSourceW failed %lu\n"), GetLastError()); +#endif + return; + } + + switch ( MessageId >> 30 ) + { + case STATUS_SEVERITY_INFORMATIONAL: + case STATUS_SEVERITY_SUCCESS: + wEventType = EVENTLOG_INFORMATION_TYPE; + break; + case STATUS_SEVERITY_WARNING: + wEventType = EVENTLOG_WARNING_TYPE; + break; + case STATUS_SEVERITY_ERROR: + default: + wEventType = EVENTLOG_ERROR_TYPE; + break; + } + + if (ErrorCode == ERROR_SUCCESS) { + + // + // No error codes were specified + // + (void) ReportEventW( + LogHandle, + wEventType, + 0, // event category + MessageId, + NULL, + (WORD)NumberOfSubStrings, + 0, + SubStrings, + (PVOID) NULL + ); + + } + else { + + // + // Log the error code specified + // + (void) ReportEventW( + LogHandle, + wEventType, + 0, // event category + MessageId, + NULL, + (WORD)NumberOfSubStrings, + sizeof(DWORD), + SubStrings, + (PVOID) &ErrorCode + ); + } + + DeregisterEventSource(LogHandle); +} // LogEvent + + +///////////////////////////////////////////////////////////////////////// +DWORD WinNtBuildNumberGet( LPTSTR pszServerName, LPDWORD pdwBuildNumber ) + +/*++ + +Routine Description: + + Retrieve the build number of Windows NT running on a given machine. + +Arguments: + + pszServerName (LPTSTR) + Name of the server to check. + pdwBuildNumber (LPDWORD) + On return, holds the build number of the server (e.g., 1057 for the + release version of Windows NT 3.51). + +Return Value: + + ERROR_SUCCESS or Win error code. + +--*/ + +{ + LONG lError; + HKEY hKeyLocalMachine; + + lError = RegConnectRegistry( pszServerName, HKEY_LOCAL_MACHINE, &hKeyLocalMachine ); + + if ( ERROR_SUCCESS != lError ) + { +#if DBG + dprintf( TEXT("WinNtBuildNumberGet(): Could not connect to remote registry, error %ld.\n"), lError ); +#endif + } + else + { + HKEY hKeyCurrentVersion; + + lError = RegOpenKeyEx( hKeyLocalMachine, + TEXT( "Software\\Microsoft\\Windows NT\\CurrentVersion" ), + 0, + KEY_READ, + &hKeyCurrentVersion ); + + if ( ERROR_SUCCESS != lError ) + { +#if DBG + dprintf( TEXT("WinNtBuildNumberGet(): Could not open key, error %ld.\n"), lError ); +#endif + } + else + { + DWORD dwType; + TCHAR szWinNtBuildNumber[ 16 ]; + DWORD cbWinNtBuildNumber = sizeof( szWinNtBuildNumber ); + + lError = RegQueryValueEx( hKeyCurrentVersion, + TEXT( "CurrentBuildNumber" ), + NULL, + &dwType, + (LPBYTE) &szWinNtBuildNumber, + &cbWinNtBuildNumber ); + + if ( ERROR_SUCCESS != lError ) + { +#if DBG + dprintf( TEXT("WinNtBuildNumberGet(): Could not query value, error %ld.\n"), lError ); +#endif + } + else + { + *pdwBuildNumber = (DWORD) _wtol( szWinNtBuildNumber ); + } + + RegCloseKey( hKeyCurrentVersion ); + } + + RegCloseKey( hKeyLocalMachine ); + } + + return (DWORD) lError; +} + + +#if DBG + +///////////////////////////////////////////////////////////////////////// +LPTSTR +TimeToString( + ULONG Seconds + ) +{ + TIME_FIELDS tf; + LARGE_INTEGER Time, LTime; + static TCHAR TimeString[100]; + + if ( 0 == Seconds ) + { + lstrcpy(TimeString, TEXT("None")); + } + else + { + RtlSecondsSince1980ToTime(Seconds, &Time); + RtlSystemTimeToLocalTime(&Time, <ime); + RtlTimeToTimeFields(<ime, &tf); + + wsprintf(TimeString, TEXT("%02hd/%02hd/%04hd @ %02hd:%02hd:%02hd"), tf.Month, tf.Day, tf.Year, tf.Hour, tf.Minute, tf.Second); + } + + return TimeString; + +} // TimeToString + +#endif + diff --git a/private/net/svcdlls/lls/server/llsutil.h b/private/net/svcdlls/lls/server/llsutil.h new file mode 100644 index 000000000..ae4feb24a --- /dev/null +++ b/private/net/svcdlls/lls/server/llsutil.h @@ -0,0 +1,63 @@ +/*++ + +Copyright (c) 1995 Microsoft Corporation + +Module Name: + + LlsUtil.h + +Abstract: + + +Author: + + Arthur Hanson (arth) Dec 07, 1994 + +Environment: + +Revision History: + + Jeff Parham (jeffparh) 12-Jan-1996 + o Added WinNtBuildNumberGet() to ascertain the Windows NT build number + running on a given machine. + +--*/ + + +#ifndef _LLS_LLSUTIL_H +#define _LLS_LLSUTIL_H + + +#ifdef __cplusplus +extern "C" { +#endif + +NTSTATUS EBlock( PVOID Data, ULONG DataSize ); +NTSTATUS DeBlock( PVOID Data, ULONG DataSize ); + +BOOL FileExists( LPTSTR FileName ); +VOID lsplitpath( const TCHAR *path, TCHAR *drive, TCHAR *dir, TCHAR *fname, TCHAR *ext ); +VOID lmakepath( TCHAR *path, const TCHAR *drive, const TCHAR *dir, const TCHAR *fname, const TCHAR *ext ); +VOID FileBackupCreate( LPTSTR Path ); +HANDLE LlsFileInit( LPTSTR FileName, DWORD Version, DWORD DataSize ); +HANDLE LlsFileCheck( LPTSTR FileName, LPDWORD Version, LPDWORD DataSize ); + +DWORD DateSystemGet( ); +DWORD DateLocalGet( ); +DWORD InAWorkgroup( VOID ); +VOID LogEvent( DWORD MessageId, DWORD NumberOfSubStrings, LPWSTR *SubStrings, DWORD ErrorCode ); + +DWORD WinNtBuildNumberGet( LPTSTR pszServerName, LPDWORD pdwBuildNumber ); + +#if DBG + +LPTSTR TimeToString( ULONG Seconds ); + +#endif + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/private/net/svcdlls/lls/server/makefile b/private/net/svcdlls/lls/server/makefile new file mode 100644 index 000000000..6ee4f43fa --- /dev/null +++ b/private/net/svcdlls/lls/server/makefile @@ -0,0 +1,6 @@ +# +# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source +# file to this component. This file merely indirects to the real make file +# that is shared by all the components of NT OS/2 +# +!INCLUDE $(NTMAKEENV)\makefile.def diff --git a/private/net/svcdlls/lls/server/makefile.inc b/private/net/svcdlls/lls/server/makefile.inc new file mode 100644 index 000000000..fc603fc9b --- /dev/null +++ b/private/net/svcdlls/lls/server/makefile.inc @@ -0,0 +1 @@ +obj\$(TARGET_DIRECTORY)\llssrv.res: $(SOURCES_PATH)llssrv.rc diff --git a/private/net/svcdlls/lls/server/mapping.c b/private/net/svcdlls/lls/server/mapping.c new file mode 100644 index 000000000..fe3dc4645 --- /dev/null +++ b/private/net/svcdlls/lls/server/mapping.c @@ -0,0 +1,756 @@ +/*++ + +Copyright (c) 1995 Microsoft Corporation + +Module Name: + + Mapping.c + +Abstract: + + +Author: + + Arthur Hanson (arth) Dec 07, 1994 + +Environment: + +Revision History: + +--*/ + +#include <stdlib.h> +#include <nt.h> +#include <ntrtl.h> +#include <nturtl.h> + +#include <windows.h> + +#include "debug.h" +#include "llsutil.h" +#include "llssrv.h" +#include "mapping.h" +#include "msvctbl.h" +#include "svctbl.h" +#include "perseat.h" + +#define NO_LLS_APIS +#include "llsapi.h" + + +ULONG MappingListSize = 0; +PMAPPING_RECORD *MappingList = NULL; +RTL_RESOURCE MappingListLock; + + +///////////////////////////////////////////////////////////////////////// +VOID +MappingListInit() + +/*++ + +Routine Description: + + + The table is linear so a binary search can be used on the table. We + assume that adding new Mappings is a relatively rare occurance, since + we need to sort it each time. + + The Mapping table is guarded by a read and write semaphore. Multiple + reads can occur, but a write blocks everything. + +Arguments: + + None. + +Return Value: + + None. + +--*/ + +{ + PMAPPING_RECORD Mapping; + + RtlInitializeResource(&MappingListLock); + + +} // MappingListInit + + +///////////////////////////////////////////////////////////////////////// +int __cdecl MappingListCompare(const void *arg1, const void *arg2) { + PMAPPING_RECORD Svc1, Svc2; + + Svc1 = (PMAPPING_RECORD) *((PMAPPING_RECORD *) arg1); + Svc2 = (PMAPPING_RECORD) *((PMAPPING_RECORD *) arg2); + + return lstrcmpi( Svc1->Name, Svc2->Name); + +} // MappingListCompare + + +///////////////////////////////////////////////////////////////////////// +int __cdecl MappingUserListCompare(const void *arg1, const void *arg2) { + LPTSTR User1, User2; + + User1 = (LPTSTR) *((LPTSTR *) arg1); + User2 = (LPTSTR) *((LPTSTR *) arg2); + + return lstrcmpi( User1, User2); + +} // MappingUserListCompare + + +///////////////////////////////////////////////////////////////////////// +PMAPPING_RECORD +MappingListFind( + LPTSTR MappingName + ) + +/*++ + +Routine Description: + + Internal routine to actually do binary search on MappingList, this + does not do any locking as we expect the wrapper routine to do this. + The search is a simple binary search. + +Arguments: + + MappingName - + +Return Value: + + Pointer to found Mapping table entry or NULL if not found. + +--*/ + +{ + LONG begin = 0; + LONG end = (LONG) MappingListSize - 1; + LONG cur; + int match; + PMAPPING_RECORD Mapping; + +#if DBG + if (TraceFlags & TRACE_FUNCTION_TRACE) + dprintf(TEXT("LLS TRACE: MappingListFind\n")); +#endif + + if ((MappingName == NULL) || (MappingListSize == 0)) + return NULL; + + while (end >= begin) { + // go halfway in-between + cur = (begin + end) / 2; + Mapping = MappingList[cur]; + + // compare the two result into match + match = lstrcmpi(MappingName, Mapping->Name); + + if (match < 0) + // move new begin + end = cur - 1; + else + begin = cur + 1; + + if (match == 0) + return Mapping; + } + + return NULL; + +} // MappingListFind + + +///////////////////////////////////////////////////////////////////////// +LPTSTR +MappingUserListFind( + LPTSTR User, + ULONG NumEntries, + LPTSTR *Users + ) + +/*++ + +Routine Description: + + Internal routine to actually do binary search on MasterServiceList, this + does not do any locking as we expect the wrapper routine to do this. + The search is a simple binary search. + +Arguments: + + ServiceName - + +Return Value: + + Pointer to found service table entry or NULL if not found. + +--*/ + +{ + LONG begin = 0; + LONG end; + LONG cur; + int match; + LPTSTR pUser; + +#if DBG + if (TraceFlags & TRACE_FUNCTION_TRACE) + dprintf(TEXT("LLS TRACE: MappingUserListFind\n")); +#endif + + if (NumEntries == 0) + return NULL; + + end = (LONG) NumEntries - 1; + + while (end >= begin) { + // go halfway in-between + cur = (begin + end) / 2; + pUser = Users[cur]; + + // compare the two result into match + match = lstrcmpi(User, pUser); + + if (match < 0) + // move new begin + end = cur - 1; + else + begin = cur + 1; + + if (match == 0) + return pUser; + } + + return NULL; + +} // MappingUserListFind + + +///////////////////////////////////////////////////////////////////////// +PMAPPING_RECORD +MappingListUserFind( LPTSTR User ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ + +{ + ULONG i = 0; + PMAPPING_RECORD pMap = NULL; + + // + // Need to scan list so get read access. + // + RtlAcquireResourceShared(&MappingListLock, TRUE); + + if (MappingList == NULL) + goto MappingListUserFindExit; + + while ((i < MappingListSize) && (pMap == NULL)) { + if (MappingUserListFind(User, MappingList[i]->NumMembers, MappingList[i]->Members ) != NULL) + pMap = MappingList[i]; + i++; + } + +MappingListUserFindExit: + RtlReleaseResource(&MappingListLock); + + return pMap; +} // MappingListUserFind + + +///////////////////////////////////////////////////////////////////////// +PMAPPING_RECORD +MappingListAdd( + LPTSTR MappingName, + LPTSTR Comment, + ULONG Licenses + ) + +/*++ + +Routine Description: + + Adds a Mapping to the Mapping table. + +Arguments: + + MappingName - + +Return Value: + + Pointer to added Mapping table entry, or NULL if failed. + +--*/ + +{ + PMAPPING_RECORD NewMapping; + LPTSTR NewMappingName; + LPTSTR NewComment; + PMAPPING_RECORD CurrentRecord = NULL; + +#if DBG + if (TraceFlags & TRACE_FUNCTION_TRACE) + dprintf(TEXT("LLS TRACE: MappingListAdd\n")); +#endif + + // + // We do a double check here to see if another thread just got done + // adding the Mapping, between when we checked last and actually got + // the write lock. + // + CurrentRecord = MappingListFind(MappingName); + if (CurrentRecord != NULL) { + return CurrentRecord; + } + + // + // Allocate space for table (zero init it). + // + if (MappingList == NULL) + MappingList = (PMAPPING_RECORD *) LocalAlloc(LPTR, sizeof(PMAPPING_RECORD)); + else + MappingList = (PMAPPING_RECORD *) LocalReAlloc(MappingList, sizeof(PMAPPING_RECORD) * (MappingListSize + 1), LHND); + + // + // Make sure we could allocate Mapping table + // + if (MappingList == NULL) { + MappingListSize = 0; + return NULL; + } + + NewMapping = LocalAlloc(LPTR, sizeof(MAPPING_RECORD)); + if (NewMapping == NULL) + return NULL; + + MappingList[MappingListSize] = NewMapping; + + NewMappingName = (LPTSTR) LocalAlloc(LPTR, (lstrlen(MappingName) + 1) * sizeof(TCHAR)); + if (NewMappingName == NULL) { + LocalFree(NewMapping); + return NULL; + } + + // now copy it over... + NewMapping->Name = NewMappingName; + lstrcpy(NewMappingName, MappingName); + + // + // Allocate space for Comment + // + NewComment = (LPTSTR) LocalAlloc(LPTR, (lstrlen(Comment) + 1) * sizeof(TCHAR)); + if (NewComment == NULL) { + LocalFree(NewMapping); + LocalFree(NewMappingName); + return NULL; + } + + // now copy it over... + NewMapping->Comment = NewComment; + lstrcpy(NewComment, Comment); + + NewMapping->NumMembers = 0; + NewMapping->Members = NULL; + NewMapping->Licenses = Licenses; + NewMapping->LicenseListSize = 0; + NewMapping->LicenseList = NULL; + NewMapping->Flags = (LLS_FLAG_LICENSED | LLS_FLAG_SUITE_AUTO); + + MappingListSize++; + + // Have added the entry - now need to sort it in order of the Mapping names + qsort((void *) MappingList, (size_t) MappingListSize, sizeof(PMAPPING_RECORD), MappingListCompare); + + return NewMapping; + +} // MappingListAdd + + +///////////////////////////////////////////////////////////////////////// +NTSTATUS +MappingListDelete( + LPTSTR MappingName + ) + +/*++ + +Routine Description: + +Arguments: + + MappingName - + +Return Value: + + +--*/ + +{ + PMAPPING_RECORD Mapping; + ULONG i; + +#if DBG + if (TraceFlags & TRACE_FUNCTION_TRACE) + dprintf(TEXT("LLS TRACE: MappingListDelete\n")); +#endif + + // + // Get mapping record based on name given + // + Mapping = MappingListFind(MappingName); + if (Mapping == NULL) + return STATUS_OBJECT_NAME_NOT_FOUND; + + // + // Make sure there are no members to the mapping + // + if (Mapping->NumMembers != 0) + return STATUS_MEMBER_IN_GROUP; + + // + // Check if this is the last Mapping + // + if (MappingListSize == 1) { + LocalFree(Mapping->Name); + LocalFree(Mapping->Comment); + LocalFree(Mapping); + LocalFree(MappingList); + MappingListSize = 0; + MappingList = NULL; + return STATUS_SUCCESS; + } + + // + // Not the last mapping so find it in the list + // + i = 0; + while ((i < MappingListSize) && (lstrcmpi(MappingList[i]->Name, MappingName))) + i++; + + // + // Now move everything below it up. + // + i++; + while (i < MappingListSize) { + memcpy(&MappingList[i-1], &MappingList[i], sizeof(PMAPPING_RECORD)); + i++; + } + + MappingList = (PMAPPING_RECORD *) LocalReAlloc(MappingList, sizeof(PMAPPING_RECORD) * (MappingListSize - 1), LHND); + + // + // Make sure we could allocate Mapping table + // + ASSERT(MappingList != NULL); + if (MappingList == NULL) + MappingListSize = 0; + else + MappingListSize--; + + // + // Now free up the record + // + LocalFree(Mapping->Name); + LocalFree(Mapping->Comment); + LocalFree(Mapping); + + return STATUS_SUCCESS; + +} // MappingListDelete + + +///////////////////////////////////////////////////////////////////////// +PMAPPING_RECORD +MappingUserListAdd( + LPTSTR MappingName, + LPTSTR User + ) + +/*++ + +Routine Description: + + +Arguments: + + MappingName - + +Return Value: + + Pointer to added Mapping table entry, or NULL if failed. + +--*/ + +{ + PMAPPING_RECORD Mapping; + LPTSTR NewName; + LPTSTR pUser; + +#if DBG + if (TraceFlags & TRACE_FUNCTION_TRACE) + dprintf(TEXT("LLS TRACE: MappingUserListAdd\n")); +#endif + + // + // Get mapping record based on name given + // + Mapping = MappingListFind(MappingName); + if (Mapping == NULL) + return NULL; + + // + // We do a double check here to see if another thread just got done + // adding the Mapping, between when we checked last and actually got + // the write lock. + // + pUser = MappingUserListFind(User, Mapping->NumMembers, Mapping->Members); + + if (pUser != NULL) { + return Mapping; + } + + // + // Allocate space for table (zero init it). + // + if (Mapping->Members == NULL) + Mapping->Members = (LPTSTR *) LocalAlloc(LPTR, sizeof(LPTSTR)); + else + Mapping->Members = (LPTSTR *) LocalReAlloc(Mapping->Members, sizeof(LPTSTR) * (Mapping->NumMembers + 1), LHND); + + // + // Make sure we could allocate Mapping table + // + if (Mapping->Members == NULL) { + Mapping->NumMembers = 0; + return NULL; + } + + NewName = (LPTSTR) LocalAlloc(LPTR, (lstrlen(User) + 1) * sizeof(TCHAR)); + if (NewName == NULL) + return NULL; + + // now copy it over... + Mapping->Members[Mapping->NumMembers] = NewName; + lstrcpy(NewName, User); + + Mapping->NumMembers++; + + // Have added the entry - now need to sort it in order of the Mapping names + qsort((void *) Mapping->Members, (size_t) Mapping->NumMembers, sizeof(LPTSTR), MappingUserListCompare); + + return Mapping; + +} // MappingUserListAdd + + +///////////////////////////////////////////////////////////////////////// +NTSTATUS +MappingUserListDelete( + LPTSTR MappingName, + LPTSTR User + ) + +/*++ + +Routine Description: + + +Arguments: + + MappingName - + +Return Value: + + Pointer to added Mapping table entry, or NULL if failed. + +--*/ + +{ + PMAPPING_RECORD Mapping; + LPTSTR pUser; + ULONG i; + +#if DBG + if (TraceFlags & TRACE_FUNCTION_TRACE) + dprintf(TEXT("LLS TRACE: MappingUserListDelete\n")); +#endif + + // + // Get mapping record based on name given + // + Mapping = MappingListFind(MappingName); + if (Mapping == NULL) + return STATUS_OBJECT_NAME_NOT_FOUND; + + // + // Find the given user + // + pUser = MappingUserListFind(User, Mapping->NumMembers, Mapping->Members); + if (pUser == NULL) + return STATUS_OBJECT_NAME_NOT_FOUND; + + // + // Check if this is the last user + // + if (Mapping->NumMembers == 1) { + LocalFree(pUser); + LocalFree(Mapping->Members); + Mapping->Members = NULL; + Mapping->NumMembers = 0; + return STATUS_SUCCESS; + } + + // + // Not the last member so find it in the list + // + i = 0; + while ((i < Mapping->NumMembers) && (lstrcmpi(Mapping->Members[i], User))) + i++; + + // + // Now move everything below it up. + // + i++; + while (i < Mapping->NumMembers) { + memcpy(&Mapping->Members[i-1], &Mapping->Members[i], sizeof(LPTSTR)); + i++; + } + + Mapping->Members = (LPTSTR *) LocalReAlloc(Mapping->Members, sizeof(LPTSTR) * (Mapping->NumMembers - 1), LHND); + + // + // Make sure we could allocate Mapping table + // + ASSERT(Mapping->Members != NULL); + if (Mapping->Members == NULL) + Mapping->NumMembers = 0; + else + Mapping->NumMembers--; + + LocalFree(pUser); + return STATUS_SUCCESS; + +} // MappingUserListDelete + + +#if DBG +///////////////////////////////////////////////////////////////////////// +VOID +MappingListDebugDump( ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ + +{ + ULONG i = 0; + + // + // Need to scan list so get read access. + // + RtlAcquireResourceShared(&MappingListLock, TRUE); + + dprintf(TEXT("Mapping Table, # Entries: %lu\n"), MappingListSize); + if (MappingList == NULL) + goto MappingListDebugDumpExit; + + for (i = 0; i < MappingListSize; i++) { + dprintf(TEXT(" Name: %s Flags: 0x%4lX LT: %2lu Lic: %4lu # Mem: %4lu Comment: %s\n"), + MappingList[i]->Name, MappingList[i]->Flags, MappingList[i]->LicenseListSize, MappingList[i]->Licenses, MappingList[i]->NumMembers, MappingList[i]->Comment); + } + +MappingListDebugDumpExit: + RtlReleaseResource(&MappingListLock); + + return; +} // MappingListDebugDump + + +///////////////////////////////////////////////////////////////////////// +VOID +MappingListDebugInfoDump( PVOID Data ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ + +{ + ULONG i; + PMAPPING_RECORD Mapping = NULL; + + RtlAcquireResourceShared(&MappingListLock, TRUE); + dprintf(TEXT("Mapping Table, # Entries: %lu\n"), MappingListSize); + + if (Data == NULL) + goto MappingListDebugInfoDumpExit; + + if (MappingList == NULL) + goto MappingListDebugInfoDumpExit; + + if (lstrlen((LPWSTR) Data) > 0) { + Mapping = MappingListFind((LPTSTR) Data); + + if (Mapping != NULL) { + dprintf(TEXT(" Name: %s Flags: 0x%4lX LT: %2lu Lic: %4lu # Mem: %4lu Comment: %s\n"), + Mapping->Name, Mapping->Flags, Mapping->LicenseListSize, Mapping->Licenses, Mapping->NumMembers, Mapping->Comment); + + if (Mapping->NumMembers != 0) + dprintf(TEXT("\nMembers\n")); + + for (i = 0; i < Mapping->NumMembers; i++) + dprintf(TEXT(" %s\n"), Mapping->Members[i]); + + if (Mapping->LicenseListSize != 0) + dprintf(TEXT("\nLicenseTable\n")); + + for (i = 0; i < Mapping->LicenseListSize; i++) + dprintf( TEXT(" Flags: 0x%4lX Ref: %2lu LN: %2lu Svc: %s\n"), + Mapping->LicenseList[i]->Flags, + Mapping->LicenseList[i]->RefCount, + Mapping->LicenseList[i]->LicensesNeeded, + Mapping->LicenseList[i]->Service->Name ); + + } else + dprintf(TEXT("Mapping not found: %s\n"), (LPTSTR) Data); + } + +MappingListDebugInfoDumpExit: + RtlReleaseResource(&MappingListLock); + +} // MappingListDebugInfoDump + +#endif + + diff --git a/private/net/svcdlls/lls/server/mapping.h b/private/net/svcdlls/lls/server/mapping.h new file mode 100644 index 000000000..29fb4289b --- /dev/null +++ b/private/net/svcdlls/lls/server/mapping.h @@ -0,0 +1,70 @@ +/*++ + +Copyright (c) 1994 Microsoft Corporation + +Module Name: + + Mapping.h + +Abstract: + + +Author: + + Arthur Hanson (arth) Dec 07, 1994 + +Environment: + +Revision History: + +--*/ + + +#ifndef _LLS_MAPPING_H +#define _LLS_MAPPING_H + + +#ifdef __cplusplus +extern "C" { +#endif + +struct _USER_LICENSE_RECORD; + +typedef struct _MAPPING_RECORD { + LPTSTR Name; + DWORD Flags; + LPTSTR Comment; + ULONG Licenses; + ULONG NumMembers; + LPTSTR *Members; + + ULONG LicenseListSize; + struct _USER_LICENSE_RECORD **LicenseList; +} MAPPING_RECORD, *PMAPPING_RECORD; + + +VOID MappingListInit(); +PMAPPING_RECORD MappingListFind( LPTSTR MappingName ); +LPTSTR MappingUserListFind( LPTSTR User, ULONG NumEntries, LPTSTR *Users ); +PMAPPING_RECORD MappingListAdd( LPTSTR MappingName, LPTSTR Comment, ULONG Licenses ); +NTSTATUS MappingListDelete( LPTSTR MappingName ); +PMAPPING_RECORD MappingUserListAdd( LPTSTR MappingName, LPTSTR User ); +PMAPPING_RECORD MappingListUserFind( LPTSTR User ); +NTSTATUS MappingUserListDelete( LPTSTR MappingName, LPTSTR User ); + +extern ULONG MappingListSize; +extern PMAPPING_RECORD *MappingList; +extern RTL_RESOURCE MappingListLock; + +#if DBG + +VOID MappingListDebugDump(); +VOID MappingListDebugInfoDump( PVOID Data ); + +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/private/net/svcdlls/lls/server/msvctbl.c b/private/net/svcdlls/lls/server/msvctbl.c new file mode 100644 index 000000000..44e5a8124 --- /dev/null +++ b/private/net/svcdlls/lls/server/msvctbl.c @@ -0,0 +1,712 @@ +/*++ + +Copyright (c) 1994 Microsoft Corporation + +Module Name: + + msvctbl.c + +Abstract: + + Master Service Table routines. Handles all access to the master service + table kept for per-seat information. + + =========================== DATA STRUCTURES ============================= + + MasterServiceTable (PMASTER_SERVICE_RECORD *) + MasterServiceList (PMASTER_SERVICE_RECORD *) + Each of these points to an array of pointers to dynamically allocated + MASTER_SERVICE_RECORDs. There is exactly one MASTER_SERVICE_RECORD + for each (product, version) pairing; e.g., (SQL 4.0, SNA 2.0, + SNA 2.1). The MasterServiceTable is never re-ordered, so a valid + index into this table is guaranteed to always dereference to the same + (product, version). The MasterServiceList contains the same data + sorted lexicographically by product name (and therefore the data + pointed to by a specific index may change over time as new + (product, version) pairs are added to the table). Each table + contains MasterServiceListSize entries. + + RootServiceList (PMASTER_SERVICE_ROOT *) + This points to an array of pointers to dynamically allocated + MASTER_SERVICE_ROOTs. There is exactly one MASTER_SERVICE_ROOT + for each product family. Each MASTER_SERVICE_ROOT contains a + pointer to an array of indices into the MasterServiceTable + corresponding to all the products in the family, sorted by + ascending version number. The RootServiceList itself is + sorted lexicographically by ascending family name. It contains + RootServiceListSize entries. + +Author: + + Arthur Hanson (arth) 07-Dec-1994 + +Revision History: + + Jeff Parham (jeffparh) 05-Dec-1995 + o Added a few comments. + o Added parameter to LicenseServiceListFind(). + o Fixed benign bug in memory allocation: + sizeof(PULONG) -> sizeof(ULONG). + +--*/ + +#include <stdlib.h> +#include <nt.h> +#include <ntrtl.h> +#include <nturtl.h> +#include <windows.h> + +#include "llsapi.h" +#include "debug.h" +#include "llssrv.h" +#include "registry.h" +#include "ntlsapi.h" +#include "mapping.h" +#include "msvctbl.h" +#include "svctbl.h" +#include "purchase.h" +#include "perseat.h" + + +///////////////////////////////////////////////////////////////////////// +// +// Master Service Table - Keeps list of products (SQL, SNA, etc.) with a +// sub-list for each version of the product. +// + +///////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////// + +#define DEFAULT_SERVICE_TABLE_ENTRIES 10 + +ULONG RootServiceListSize = 0; +PMASTER_SERVICE_ROOT *RootServiceList = NULL; + +ULONG MasterServiceListSize = 0; +PMASTER_SERVICE_RECORD *MasterServiceList = NULL; +PMASTER_SERVICE_RECORD *MasterServiceTable = NULL; + +TCHAR BackOfficeStr[100]; +PMASTER_SERVICE_RECORD BackOffice; + + +RTL_RESOURCE MasterServiceListLock; + + +///////////////////////////////////////////////////////////////////////// +VOID +MasterServiceListInit() + +/*++ + +Routine Description: + + Creates the Master Service table, used for tracking the services and + session count. This will pull the initial services from the registry. + + The table is linear so a binary search can be used on the table, so + some extra records are initialized so that each time we add a new + service we don't have to do a realloc. We also assume that adding + new services is a relatively rare occurance, since we need to sort + it each time. + + The service table is guarded by a read and write semaphore. Multiple + reads can occur, but a write blocks everything. + + The service table has two default entries for FilePrint and REMOTE_ACCESS. + +Arguments: + + None. + +Return Value: + + None. + +--*/ + +{ + int nLen; + HANDLE LlsDLLHandle = NULL; + + RtlInitializeResource(&MasterServiceListLock); + + memset(BackOfficeStr, 0, sizeof(BackOffice)); + BackOffice = NULL; + LlsDLLHandle = LoadLibrary(TEXT("LLSRPC.DLL")); + ASSERT(LlsDLLHandle != NULL); + + if (LlsDLLHandle != NULL) { + nLen = LoadString(LlsDLLHandle, IDS_BACKOFFICE, BackOfficeStr, sizeof(BackOfficeStr)); + + if (nLen != 0) { + BackOffice = MasterServiceListAdd( BackOfficeStr, BackOfficeStr, 0 ); + } else { +#if DBG + dprintf(TEXT("LLS ERROR: Could not load BackOffice string\n")); +#endif + } + + } + +} // MasterServiceListInit + + +///////////////////////////////////////////////////////////////////////// +// used by qsort to sort MasterServiceList by product name +int __cdecl MasterServiceListCompare(const void *arg1, const void *arg2) { + PMASTER_SERVICE_RECORD Svc1, Svc2; + + Svc1 = (PMASTER_SERVICE_RECORD) *((PMASTER_SERVICE_RECORD *) arg1); + Svc2 = (PMASTER_SERVICE_RECORD) *((PMASTER_SERVICE_RECORD *) arg2); + + return lstrcmpi( Svc1->Name, Svc2->Name ); + +} // MasterServiceListCompare + + +///////////////////////////////////////////////////////////////////////// +// used by qsort to sort the Services array of indices pointed to by the +// MASTER_SERVICE_ROOT structure by product version number +int __cdecl MServiceRecordCompare(const void *arg1, const void *arg2) { + PMASTER_SERVICE_RECORD Svc1, Svc2; + + Svc1 = (PMASTER_SERVICE_RECORD) MasterServiceTable[*((PULONG) arg1)]; + Svc2 = (PMASTER_SERVICE_RECORD) MasterServiceTable[*((PULONG) arg2)]; + + return (int) Svc1->Version - Svc2->Version; + +} // MServiceRecordCompare + + +///////////////////////////////////////////////////////////////////////// +// used by qsort to sort the RootServiceList array of product families +// by family name +int __cdecl MServiceRootCompare(const void *arg1, const void *arg2) { + PMASTER_SERVICE_ROOT Svc1, Svc2; + + Svc1 = (PMASTER_SERVICE_ROOT) *((PMASTER_SERVICE_ROOT *) arg1); + Svc2 = (PMASTER_SERVICE_ROOT) *((PMASTER_SERVICE_ROOT *) arg2); + + return lstrcmpi( Svc1->Name, Svc2->Name ); + +} // MServiceRootCompare + + +///////////////////////////////////////////////////////////////////////// +PMASTER_SERVICE_ROOT +MServiceRootFind( + LPTSTR ServiceName + ) + +/*++ + +Routine Description: + + Internal routine to actually do binary search on MasterServiceList, this + does not do any locking as we expect the wrapper routine to do this. + The search is a simple binary search. + +Arguments: + + ServiceName - + +Return Value: + + Pointer to found service table entry or NULL if not found. + +--*/ + +{ + LONG begin = 0; + LONG end = (LONG) RootServiceListSize - 1; + LONG cur; + int match; + PMASTER_SERVICE_ROOT ServiceRoot; + +#if DBG + if (TraceFlags & TRACE_FUNCTION_TRACE) + dprintf(TEXT("LLS TRACE: MServiceRootFind\n")); +#endif + + if ((RootServiceListSize == 0) || (ServiceName == NULL)) + return NULL; + + while (end >= begin) { + // go halfway in-between + cur = (begin + end) / 2; + ServiceRoot = RootServiceList[cur]; + + // compare the two result into match + match = lstrcmpi(ServiceName, ServiceRoot->Name); + + if (match < 0) + // move new begin + end = cur - 1; + else + begin = cur + 1; + + if (match == 0) + return ServiceRoot; + } + + return NULL; + +} // MServiceRootFind + + +///////////////////////////////////////////////////////////////////////// +PMASTER_SERVICE_RECORD +MasterServiceListFind( + LPTSTR Name + ) + +/*++ + +Routine Description: + + Internal routine to actually do binary search on MasterServiceList, this + does not do any locking as we expect the wrapper routine to do this. + The search is a simple binary search. + +Arguments: + + ServiceName - + +Return Value: + + Pointer to found service table entry or NULL if not found. + +--*/ + +{ + LONG begin = 0; + LONG end; + LONG cur; + int match; + PMASTER_SERVICE_RECORD Service; + +#if DBG + if (TraceFlags & TRACE_FUNCTION_TRACE) + dprintf(TEXT("LLS TRACE: MasterServiceListFind\n")); +#endif + + if ((Name == NULL) || (MasterServiceListSize == 0)) + return NULL; + + end = (LONG) MasterServiceListSize - 1; + + while (end >= begin) { + // go halfway in-between + cur = (begin + end) / 2; + Service = MasterServiceList[cur]; + + // compare the two result into match + match = lstrcmpi(Name, Service->Name); + + if (match < 0) + // move new begin + end = cur - 1; + else + begin = cur + 1; + + if (match == 0) + return Service; + } + + return NULL; + +} // MasterServiceListFind + + +///////////////////////////////////////////////////////////////////////// +PMASTER_SERVICE_RECORD +MasterServiceListAdd( + LPTSTR FamilyName, + LPTSTR Name, + DWORD Version + ) + +/*++ + +Routine Description: + + +Arguments: + + ServiceName - + +Return Value: + + Pointer to added service table entry, or NULL if failed. + +--*/ + +{ + ULONG i; + ULONG SessionLimit = 0; + BOOL PerSeatLicensing = FALSE; + LPTSTR NewServiceName, pDisplayName; + PMASTER_SERVICE_RECORD Service = NULL; + PMASTER_SERVICE_ROOT ServiceRoot = NULL; + PULONG ServiceList; + PLICENSE_SERVICE_RECORD pLicense; + +#if DBG + if (TraceFlags & TRACE_FUNCTION_TRACE) + dprintf(TEXT("LLS TRACE: MasterServiceListAdd\n")); +#endif + + // + // Mask off low word of version - as it doesn't matter to licensing + // + Version &= 0xFFFF0000; + + Service = MasterServiceListFind(Name); + if (Service != NULL) + return Service; + + // + // Try to find a root node for that family of products + // + ServiceRoot = MServiceRootFind(FamilyName); + + if (ServiceRoot == NULL) { + // + // No root record - so create a new one + // + if (RootServiceList == NULL) + RootServiceList = (PMASTER_SERVICE_ROOT *) LocalAlloc(LPTR, sizeof(PMASTER_SERVICE_ROOT)); + else + RootServiceList = (PMASTER_SERVICE_ROOT *) LocalReAlloc(RootServiceList, sizeof(PMASTER_SERVICE_ROOT) * (RootServiceListSize + 1), LHND); + + // + // Make sure we could allocate service table + // + if (RootServiceList == NULL) { + ASSERT(FALSE); + RootServiceListSize = 0; + return NULL; + } + + // + // Allocate space for Root. + // + ServiceRoot = (PMASTER_SERVICE_ROOT) LocalAlloc(LPTR, sizeof(MASTER_SERVICE_ROOT)); + if (ServiceRoot == NULL) { + ASSERT(FALSE); + return NULL; + } + + RootServiceList[RootServiceListSize] = ServiceRoot; + + NewServiceName = (LPTSTR) LocalAlloc(LPTR, (lstrlen(FamilyName) + 1) * sizeof(TCHAR)); + if (NewServiceName == NULL) { + ASSERT(FALSE); + LocalFree(ServiceRoot); + return NULL; + } + + // now copy it over... + ServiceRoot->Name = NewServiceName; + lstrcpy(NewServiceName, FamilyName); + + // + // Initialize stuff for list of various versions of this product + // + ServiceRoot->ServiceTableSize = 0; + ServiceRoot->Services = NULL; + ServiceRoot->Flags = 0; + + RtlInitializeResource(&ServiceRoot->ServiceLock); + + RootServiceListSize++; + + // Have added the entry - now need to sort it in order of the service names + qsort((void *) RootServiceList, (size_t) RootServiceListSize, sizeof(PMASTER_SERVICE_ROOT), MServiceRootCompare); + + } + + //////////////////////////////////////////////////////////////////////// + // + // Whether added or found, ServiceRoot points to the Root Node entry. + // Now double check to see if another thread just got done adding the + // actual service before we got the write lock. + // + RtlAcquireResourceShared(&ServiceRoot->ServiceLock, TRUE); + Service = MasterServiceListFind(Name); + + if (Service == NULL) { + // + // No Service Record - so create a new one + // + RtlConvertSharedToExclusive(&ServiceRoot->ServiceLock); + ServiceList = ServiceRoot->Services; + if (ServiceList == NULL) + ServiceList = (PULONG) LocalAlloc(LPTR, sizeof(ULONG)); + else + ServiceList = (PULONG) LocalReAlloc(ServiceList, sizeof(ULONG) * (ServiceRoot->ServiceTableSize + 1), LHND); + + if (MasterServiceList == NULL) { + MasterServiceList = (PMASTER_SERVICE_RECORD *) LocalAlloc(LPTR, sizeof(PMASTER_SERVICE_RECORD)); + MasterServiceTable = (PMASTER_SERVICE_RECORD *) LocalAlloc(LPTR, sizeof(PMASTER_SERVICE_RECORD)); + } else { + MasterServiceList = (PMASTER_SERVICE_RECORD *) LocalReAlloc(MasterServiceList, sizeof(PMASTER_SERVICE_RECORD) * (MasterServiceListSize + 1), LHND); + MasterServiceTable = (PMASTER_SERVICE_RECORD *) LocalReAlloc(MasterServiceTable, sizeof(PMASTER_SERVICE_RECORD) * (MasterServiceListSize + 1), LHND); + } + + ServiceRoot->Services = ServiceList; + + // + // Make sure we could allocate service table + // + if ((ServiceList == NULL) || (MasterServiceList == NULL) || (MasterServiceTable == NULL)) { + ASSERT(FALSE); + ServiceRoot->ServiceTableSize = 0; + MasterServiceListSize = 0; + MasterServiceList = NULL; + MasterServiceTable = NULL; + RtlReleaseResource(&ServiceRoot->ServiceLock); + return NULL; + } + + // + // Allocate space for saving off Service Record. + // + Service = (PMASTER_SERVICE_RECORD) LocalAlloc(LPTR, sizeof(MASTER_SERVICE_RECORD)); + if (Service == NULL) { + ASSERT(FALSE); + RtlReleaseResource(&ServiceRoot->ServiceLock); + return NULL; + } + + ServiceList[ServiceRoot->ServiceTableSize] = MasterServiceListSize; + MasterServiceList[MasterServiceListSize] = Service; + MasterServiceTable[MasterServiceListSize] = Service; + + // + // ...DisplayName + // + NewServiceName = (LPTSTR) LocalAlloc(LPTR, (lstrlen(Name) + 1) * sizeof(TCHAR)); + if (NewServiceName == NULL) { + ASSERT(FALSE); + LocalFree(Service); + RtlReleaseResource(&ServiceRoot->ServiceLock); + return NULL; + } + + // now copy it over... + Service->Name = NewServiceName; + lstrcpy(NewServiceName, Name); + + // + // Init rest of values. + // + Service->Version= Version; + Service->LicensesUsed = 0; + Service->LicensesClaimed = 0; + Service->next = 0; + Service->Index = MasterServiceListSize; + Service->Family = ServiceRoot; + + pLicense = LicenseServiceListFind(Service->Name, FALSE); + if (pLicense == NULL) + Service->Licenses = 0; + else + Service->Licenses = pLicense->NumberLicenses; + + // + // Init next pointer + // + i = 0; + while ((i < ServiceRoot->ServiceTableSize) && (MasterServiceTable[ServiceRoot->Services[i]]->Version < Version)) + i++; + + if (i > 0) { + Service->next = MasterServiceTable[ServiceRoot->Services[i - 1]]->next; + MasterServiceTable[ServiceRoot->Services[i - 1]]->next = Service->Index + 1; + } + + ServiceRoot->ServiceTableSize++; + MasterServiceListSize++; + + // Have added the entry - now need to sort it in order of the versions + qsort((void *) ServiceList, (size_t) ServiceRoot->ServiceTableSize, sizeof(PMASTER_SERVICE_RECORD), MServiceRecordCompare); + + // And sort the list the UI uses (sorted by service name) + qsort((void *) MasterServiceList, (size_t) MasterServiceListSize, sizeof(PMASTER_SERVICE_RECORD), MasterServiceListCompare); + } + + RtlReleaseResource(&ServiceRoot->ServiceLock); + return Service; + +} // MasterServiceListAdd + + +#if DBG + +///////////////////////////////////////////////////////////////////////// +// +// DEBUG INFORMATION DUMP ROUTINES +// +///////////////////////////////////////////////////////////////////////// + + +///////////////////////////////////////////////////////////////////////// +VOID +MasterServiceRootDebugDump( ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ + +{ + ULONG i = 0; + + // + // Need to scan list so get read access. + // + RtlAcquireResourceShared(&MasterServiceListLock, TRUE); + + dprintf(TEXT("Service Family Table, # Entries: %lu\n"), RootServiceListSize); + if (RootServiceList == NULL) + goto MasterServiceRootDebugDumpExit; + + for (i = 0; i < RootServiceListSize; i++) { + dprintf(TEXT("%3lu) Services: %3lu Svc: %s [%s]\n"), + i + 1, RootServiceList[i]->ServiceTableSize, RootServiceList[i]->Name, RootServiceList[i]->Name); + } + +MasterServiceRootDebugDumpExit: + RtlReleaseResource(&MasterServiceListLock); + + return; +} // MasterServiceRootDebugDump + + +///////////////////////////////////////////////////////////////////////// +VOID +MasterServiceRootDebugInfoDump( PVOID Data ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ + +{ + ULONG i = 0; + + // + // Need to scan list so get read access. + // + RtlAcquireResourceShared(&MasterServiceListLock, TRUE); + + dprintf(TEXT("Service Family Table, # Entries: %lu\n"), RootServiceListSize); + if (RootServiceList == NULL) + goto MasterServiceRootDebugDumpExit; + + for (i = 0; i < RootServiceListSize; i++) { + dprintf(TEXT("%3lu) Services: %3lu Svc: %s [%s]\n"), + i + 1, RootServiceList[i]->ServiceTableSize, RootServiceList[i]->Name, RootServiceList[i]->Name); + } + +MasterServiceRootDebugDumpExit: + RtlReleaseResource(&MasterServiceListLock); + + return; +} // MasterServiceRootDebugInfoDump + + +///////////////////////////////////////////////////////////////////////// +VOID +MasterServiceListDebugDump( ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ + +{ + ULONG i = 0; + + // + // Need to scan list so get read access. + // + RtlAcquireResourceShared(&MasterServiceListLock, TRUE); + + dprintf(TEXT("Master Service Table, # Entries: %lu\n"), MasterServiceListSize); + if (MasterServiceList == NULL) + goto MasterServiceListDebugDumpExit; + + for (i = 0; i < MasterServiceListSize; i++) { + dprintf(TEXT("%3lu) [%3lu] LU: %4lu LP: %4lu LC: %4lu MS: %4lu HM: %4lu Next: %3lu Svc: %s %lX\n"), + i + 1, MasterServiceList[i]->Index, + MasterServiceList[i]->LicensesUsed, MasterServiceList[i]->Licenses, MasterServiceList[i]->LicensesClaimed, + MasterServiceList[i]->MaxSessionCount, MasterServiceList[i]->HighMark, + MasterServiceList[i]->next, MasterServiceList[i]->Name, MasterServiceList[i]->Version); + } + +MasterServiceListDebugDumpExit: + RtlReleaseResource(&MasterServiceListLock); + + return; +} // MasterServiceListDebugDump + + +///////////////////////////////////////////////////////////////////////// +VOID +MasterServiceListDebugInfoDump( PVOID Data ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ + +{ + PMASTER_SERVICE_RECORD CurrentRecord = NULL; + + dprintf(TEXT("Master Service Table, # Entries: %lu\n"), RootServiceListSize); + + if (lstrlen((LPWSTR) Data) > 0) { +// CurrentRecord = MasterServiceListFind((LPWSTR) Data); + if (CurrentRecord != NULL) { + } + } + +} // MasterServiceListDebugInfoDump + +#endif diff --git a/private/net/svcdlls/lls/server/msvctbl.h b/private/net/svcdlls/lls/server/msvctbl.h new file mode 100644 index 000000000..84c19a3c0 --- /dev/null +++ b/private/net/svcdlls/lls/server/msvctbl.h @@ -0,0 +1,151 @@ +/*++ + +Copyright (c) 1994 Microsoft Corporation + +Module Name: + + msvctbl.h + +Abstract: + + See msvctbl.c + +Author: + + Arthur Hanson (arth) Dec 07, 1994 + +Environment: + +Revision History: + + Jeff Parham (jeffparh) 05-Dec-1995 + o Added comments. + +--*/ + + +#ifndef _LLS_MSVCTBL_H +#define _LLS_MSVCTBL_H + + +#ifdef __cplusplus +extern "C" { +#endif + + +#define IDS_BACKOFFICE 1500 + +///////////////////////////////////////////////////////////////////////// +// +// The master service record is for license usage tracking. We have +// A master ROOT record for a family of products (say SQL Server) and +// a sub linked-list of each of the specific versions in order of the +// version number. When we do license checking we can move on up the tree +// to higher level of licenses. +// +// There is also a mapping table kept for each of the ROOT records. This +// tracks if the mapping license count has already been used. +// +struct _MASTER_SERVICE_ROOT; + +typedef struct _MASTER_SERVICE_RECORD +{ + ULONG Index; // index at which + // a pointer to this + // structure may be + // found in the + // MasterServiceTable + + LPTSTR Name; // product name + + DWORD Version; // version of the + // product; + // major.minor -> + // (major << 16) + // | minor, e.g., + // 5.2 -> 0x50002 + + struct _MASTER_SERVICE_ROOT * Family; // pointer to the + // product family, + // e.g., "SNA 2.1" + // -> "SNA" + + ULONG Licenses; + ULONG LicensesUsed; + ULONG LicensesClaimed; + + ULONG MaxSessionCount; + ULONG HighMark; + + ULONG next; // index at which + // a pointer to the + // next ascending + // version of this + // product may be + // found in the + // MasterServiceTable + // NOTE: index is + // 1-based, so if + // next == 0 there + // are no more, and + // if non-zero then + // the next version + // is at index next-1 + +} MASTER_SERVICE_RECORD, *PMASTER_SERVICE_RECORD; + +typedef struct _MASTER_SERVICE_ROOT +{ + LPTSTR Name; // name of this product family + + DWORD Flags; + + RTL_RESOURCE ServiceLock; // lock for changes to the + // Services array (below) + + ULONG ServiceTableSize; // number of entries in Services + // array (below) + + ULONG * Services; // array of indices into the + // MasterServiceTable of the various + // (product,version) pairs + // belonging to this family; + // sorted in order of ascending + // version +} MASTER_SERVICE_ROOT, *PMASTER_SERVICE_ROOT; + +extern ULONG RootServiceListSize; +extern PMASTER_SERVICE_ROOT *RootServiceList; + +extern ULONG MasterServiceListSize; +extern PMASTER_SERVICE_RECORD *MasterServiceList; +extern PMASTER_SERVICE_RECORD *MasterServiceTable; + +extern RTL_RESOURCE MasterServiceListLock; + +extern TCHAR BackOfficeStr[]; +extern PMASTER_SERVICE_RECORD BackOffice; + + +VOID MasterServiceListInit(); +PMASTER_SERVICE_RECORD MServiceRecordFind( DWORD Version, ULONG NumServiceEntries, PULONG ServiceList ); +PMASTER_SERVICE_ROOT MServiceRootFind( LPTSTR ServiceName ); +PMASTER_SERVICE_RECORD MasterServiceListFind( LPTSTR DisplayName ); +PMASTER_SERVICE_RECORD MasterServiceListAdd( LPTSTR FamilyName, LPTSTR Name, DWORD Version ); + +#if DBG + +VOID MasterServiceRootDebugDump(); +VOID MasterServiceRootDebugInfoDump( PVOID Data ); +VOID MasterServiceListDebugDump(); +VOID MasterServiceListDebugInfoDump( PVOID Data ); + +#endif + + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/private/net/svcdlls/lls/server/pack.c b/private/net/svcdlls/lls/server/pack.c new file mode 100644 index 000000000..4c6d2b01f --- /dev/null +++ b/private/net/svcdlls/lls/server/pack.c @@ -0,0 +1,4306 @@ +/*++ + +Copyright (c) 1995 Microsoft Corporation + +Module Name: + + pack.c + +Abstract: + + +Author: + + Arthur Hanson (arth) 06-Jan-1995 + +Revision History: + + Jeff Parham (jeffparh) 05-Dec-1995 + o Added new fields to purchase record to support secure certificates. + o Unified per server purchase model with per seat purchase model for + secure certificates; per server model still done in the traditional + manner for non-secure certificates (for backwards compatibility). + o Removed assertion on LicenseAdd() failure. LicenseAdd() may + legitimately fail under certain circumstances. + o Fixed bug wherein a memory allocation failure in the LLS routines + would result in a corrupt data file (which would AV the server when + it was thereafter read). (Bug #14072.) + o Added SaveAll() function analogous to LoadAll(). + o Added support for extended user data packing/unpacking. This was + done to save the SUITE_USE flag across restarts of the service. + o Removed user table parameters from unpack routines that didn't use + them. + o Fixed ServerServiceListUnpack() to subtract out old values only when + they were previously added to the MasterServiceTable. This fixes + problems with the MaxSessionCount and HighMark tallies getting skewed. + +--*/ + +#include <stdlib.h> +#include <nt.h> +#include <ntrtl.h> +#include <nturtl.h> +#include <windows.h> + +#include "llsapi.h" +#include "debug.h" +#include "llsutil.h" +#include "llssrv.h" +#include "mapping.h" +#include "msvctbl.h" +#include "svctbl.h" +#include "perseat.h" +#include "purchase.h" +#include "server.h" + +#include "llsrpc_s.h" +#include "lsapi_s.h" +#include "llsdbg_s.h" +#include "repl.h" +#include "pack.h" +#include "llsevent.h" +#include "certdb.h" + + +int __cdecl MServiceRecordCompare(const void *arg1, const void *arg2); + +static HANDLE PurchaseFile; + +///////////////////////////////////////////////////////////////////////// +// License List +// + +///////////////////////////////////////////////////////////////////////// +VOID +LicenseListUnpackOld ( + ULONG LicenseServiceTableSize, + PPACK_LICENSE_SERVICE_RECORD LicenseServices, + + ULONG LicenseTableSize, + PPACK_LICENSE_PURCHASE_RECORD_0 Licenses + ) + +/*++ + +Routine Description: + + +Arguments: + +Return Value: + + +--*/ + +{ + NTSTATUS Status = STATUS_SUCCESS; + ULONG i; + PPACK_LICENSE_PURCHASE_RECORD_0 pLicense; + +#if DBG + if (TraceFlags & TRACE_FUNCTION_TRACE) + dprintf(TEXT("LicenseListUnpackOld: Service[%lu] License[%lu]\n"), LicenseServiceTableSize, LicenseTableSize); +#endif + + // + // Walk services table, adding any new services to our local table. + // Fix up the index pointers to match our local services. + // + RtlAcquireResourceExclusive(&LicenseListLock, TRUE); + + for (i = 0; i < LicenseTableSize; i++) { + pLicense = &Licenses[i]; + + if (pLicense->Service < LicenseServiceTableSize) + Status = LicenseAdd(LicenseServices[pLicense->Service].ServiceName, TEXT("Microsoft"), pLicense->NumberLicenses, 0, pLicense->Admin, pLicense->Comment, pLicense->Date, LLS_LICENSE_MODE_ALLOW_PER_SEAT, 0, TEXT("None"), 0, NULL ); + else { + ASSERT(FALSE); + } + + if (Status != STATUS_SUCCESS) { +#ifdef DBG + dprintf(TEXT("LicenseAdd failed: 0x%lX\n"), Status); +#endif + // ASSERT(FALSE); + } + + } + + RtlReleaseResource(&LicenseListLock); + +} // LicenseListUnpackOld + + +///////////////////////////////////////////////////////////////////////// +VOID +LicenseListStringsUnpackOld ( + ULONG LicenseServiceTableSize, + PPACK_LICENSE_SERVICE_RECORD LicenseServices, + + ULONG LicenseServiceStringSize, + LPTSTR LicenseServiceStrings, + + ULONG LicenseTableSize, + PPACK_LICENSE_PURCHASE_RECORD_0 Licenses, + + ULONG LicenseStringSize, + LPTSTR LicenseStrings + ) + +/*++ + +Routine Description: + + +Arguments: + +Return Value: + + +--*/ + +{ + ULONG i; + PPACK_LICENSE_SERVICE_RECORD pSvc; + PPACK_LICENSE_PURCHASE_RECORD_0 pLicense; + TCHAR *pStr; + +#if DBG + if (TraceFlags & TRACE_FUNCTION_TRACE) + dprintf(TEXT("LicenseListStringsUnpack\n")); +#endif + + // + // First do license service strings + // + pStr = LicenseServiceStrings; + for (i = 0; i < LicenseServiceTableSize; i++) { + pSvc = &LicenseServices[i]; + + pSvc->ServiceName = pStr; + + // + // Move to end of current string + // + while (*pStr != TEXT('\0')) + pStr++; + + // now go past ending NULL + pStr++; + } + + // + // Now do license purchase strings + // + pStr = LicenseStrings; + for (i = 0; i < LicenseTableSize; i++) { + pLicense = &Licenses[i]; + + pLicense->Admin = pStr; + + // + // Move to end of current string + // + while (*pStr != TEXT('\0')) + pStr++; + + // now go past ending NULL + pStr++; + + pLicense->Comment = pStr; + + // + // Move to end of current string + // + while (*pStr != TEXT('\0')) + pStr++; + + // now go past ending NULL + pStr++; + } + +} // LicenseListStringsUnpackOld + + +///////////////////////////////////////////////////////////////////////// +VOID +LicenseListLoadOld() + +/*++ + +Routine Description: + +Arguments: + + None. + +Return Value: + + None. + +--*/ + +{ + BOOL ret; + DWORD Version, DataSize; + NTSTATUS Status = STATUS_SUCCESS; + HANDLE hFile = NULL; + + ULONG LicenseServiceTableSize; + PPACK_LICENSE_SERVICE_RECORD LicenseServices = NULL; + + ULONG LicenseServiceStringSize; + LPTSTR LicenseServiceStrings = NULL; + + ULONG LicenseTableSize; + PPACK_LICENSE_PURCHASE_RECORD_0 Licenses = NULL; + + ULONG LicenseStringSize; + LPTSTR LicenseStrings = NULL; + + LICENSE_FILE_HEADER_0 FileHeader; + DWORD BytesRead; + +#if DBG + if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_DATABASE)) + dprintf(TEXT("LLS TRACE: LicenseListLoad\n")); +#endif + + // + // Check if we already have file open + // + if (PurchaseFile != NULL) { + CloseHandle(PurchaseFile); + PurchaseFile = NULL; + } + + // + // If nothing to load then get-out + // + if (!FileExists(LicenseFileName)) + goto LicenseListLoadExit; + + // + // Check the init header + // + Version = DataSize = 0; + PurchaseFile = LlsFileCheck(LicenseFileName, &Version, &DataSize ); + if (PurchaseFile == NULL) { + Status = GetLastError(); + goto LicenseListLoadExit; + } + + if ((Version != LICENSE_FILE_VERSION_0) || (DataSize != sizeof(LICENSE_FILE_HEADER_0))) { + Status = STATUS_FILE_INVALID; + goto LicenseListLoadExit; + } + + // + // The init header checks out, so load the license header and data blocks + // + hFile = PurchaseFile; + ret = ReadFile(hFile, &FileHeader, sizeof(LICENSE_FILE_HEADER_0), &BytesRead, NULL); + + LicenseServiceTableSize = 0; + LicenseServiceStringSize = 0; + LicenseTableSize = 0; + LicenseStringSize = 0; + + if (ret) { + // + // Run through and allocate space to read data blocks into + // + if (FileHeader.LicenseServiceTableSize != 0) { + LicenseServiceTableSize = FileHeader.LicenseServiceTableSize / sizeof(PACK_LICENSE_SERVICE_RECORD); + LicenseServices = MIDL_user_allocate(FileHeader.LicenseServiceTableSize); + + if ( LicenseServices == NULL ) { + Status = STATUS_NO_MEMORY; + goto LicenseListLoadExit; + } + } + + if (FileHeader.LicenseServiceStringSize != 0) { + LicenseServiceStringSize = FileHeader.LicenseServiceStringSize / sizeof(TCHAR); + LicenseServiceStrings = MIDL_user_allocate(FileHeader.LicenseServiceStringSize); + + if ( LicenseServiceStrings == NULL ) { + Status = STATUS_NO_MEMORY; + goto LicenseListLoadExit; + } + } + + if (FileHeader.LicenseTableSize != 0) { + LicenseTableSize = FileHeader.LicenseTableSize / sizeof(PACK_LICENSE_PURCHASE_RECORD); + Licenses = MIDL_user_allocate(FileHeader.LicenseTableSize); + + if ( Licenses == NULL ) { + Status = STATUS_NO_MEMORY; + goto LicenseListLoadExit; + } + } + + if (FileHeader.LicenseStringSize != 0) { + LicenseStringSize = FileHeader.LicenseStringSize / sizeof(TCHAR); + LicenseStrings = MIDL_user_allocate(FileHeader.LicenseStringSize); + + if ( LicenseStrings == NULL ) { + Status = STATUS_NO_MEMORY; + goto LicenseListLoadExit; + } + } + + } + + if (ret && (FileHeader.LicenseServiceTableSize != 0) ) + ret = ReadFile(hFile, LicenseServices, FileHeader.LicenseServiceTableSize, &BytesRead, NULL); + + if (ret && (FileHeader.LicenseServiceStringSize != 0) ) + ret = ReadFile(hFile, LicenseServiceStrings, FileHeader.LicenseServiceStringSize, &BytesRead, NULL); + + if (ret && (FileHeader.LicenseTableSize != 0) ) + ret = ReadFile(hFile, Licenses, FileHeader.LicenseTableSize, &BytesRead, NULL); + + if (ret && (FileHeader.LicenseStringSize != 0) ) + ret = ReadFile(hFile, LicenseStrings, FileHeader.LicenseStringSize, &BytesRead, NULL); + + if (!ret) { + Status = GetLastError(); + goto LicenseListLoadExit; + } + + // + // Decrypt the data + // + Status = DeBlock(LicenseServices, FileHeader.LicenseServiceTableSize); + + if (Status == STATUS_SUCCESS) + Status = DeBlock(LicenseServiceStrings, FileHeader.LicenseServiceStringSize); + + if (Status == STATUS_SUCCESS) + Status = DeBlock(Licenses, FileHeader.LicenseTableSize); + + if (Status == STATUS_SUCCESS) + Status = DeBlock(LicenseStrings, FileHeader.LicenseStringSize); + + if (Status != STATUS_SUCCESS) + goto LicenseListLoadExit; + + + // + // Unpack the string data + // + LicenseListStringsUnpackOld( LicenseServiceTableSize, LicenseServices, + LicenseServiceStringSize, LicenseServiceStrings, + LicenseTableSize, Licenses, + LicenseStringSize, LicenseStrings ); + + // + // Unpack the license data + // + LicenseListUnpackOld( LicenseServiceTableSize, LicenseServices, LicenseTableSize, Licenses ); + +LicenseListLoadExit: + + // Note: Don't close the License Purchase File (keep it locked). + + // + // Run through our tables and clean them up + // + if (LicenseServices != NULL) + MIDL_user_free(LicenseServices); + + if (LicenseServiceStrings != NULL) + MIDL_user_free(LicenseServiceStrings); + + if (Licenses != NULL) + MIDL_user_free(Licenses); + + if (LicenseStrings != NULL) + MIDL_user_free(LicenseStrings); + + // + // If there was an error log it. + // + if (Status != STATUS_SUCCESS) + LogEvent(LLS_EVENT_LOAD_LICENSE, 0, NULL, Status); + +} // LicenseListLoadOld + +///////////////////////////////////////////////////////////////////////// +NTSTATUS +LicenseListPack ( + ULONG *pLicenseServiceTableSize, + PPACK_LICENSE_SERVICE_RECORD *pLicenseServices, + + ULONG *pLicenseTableSize, + PPACK_LICENSE_PURCHASE_RECORD *pLicenses, + + ULONG *pPerServerLicenseServiceTableSize, + PPACK_LICENSE_SERVICE_RECORD *pPerServerLicenseServices + ) + +/*++ + +Routine Description: + + +Arguments: + +Return Value: + + +--*/ + +{ + NTSTATUS Status = STATUS_SUCCESS; + PPACK_LICENSE_SERVICE_RECORD LicenseServices = NULL; + PPACK_LICENSE_PURCHASE_RECORD Licenses = NULL; + ULONG i; + ULONG TotalRecords = 0; + PLICENSE_SERVICE_RECORD pLicenseService; + PLICENSE_PURCHASE_RECORD pLicense; + PPACK_LICENSE_SERVICE_RECORD PerServerLicenseServices = NULL; + PLICENSE_SERVICE_RECORD pPerServerLicenseService; + +#if DBG + if (TraceFlags & TRACE_FUNCTION_TRACE) + dprintf(TEXT("LLS TRACE: LicenseListPack\n")); +#endif + + ASSERT(pLicenseServices != NULL); + ASSERT(pLicenseServiceTableSize != NULL); + + *pLicenseServices = NULL; + *pLicenseServiceTableSize = 0; + + ASSERT(pLicenses != NULL); + ASSERT(pLicenseTableSize != NULL); + + *pLicenses = NULL; + *pLicenseTableSize = 0; + + ASSERT(pPerServerLicenseServices != NULL); + ASSERT(pPerServerLicenseServiceTableSize != NULL); + + *pPerServerLicenseServices = NULL; + *pPerServerLicenseServiceTableSize = 0; + + ////////////////////////////////////////////////////////////////// + // + // Do License Service Table First + // + TotalRecords = LicenseServiceListSize; + + // + // Make sure there is anything to replicate + // + if (TotalRecords > 0) { + // + // Create our buffer to hold all of the garbage + // + LicenseServices = MIDL_user_allocate(TotalRecords * sizeof(PACK_LICENSE_SERVICE_RECORD)); + if (LicenseServices == NULL) { + ASSERT(FALSE); + return STATUS_NO_MEMORY; + } + + // + // Fill in the buffer - walk the License Services tree + // + for (i = 0; i < LicenseServiceListSize; i++) { + pLicenseService = LicenseServiceList[i]; + + // + // Make index match table in it's current state + // + pLicenseService->Index = i; + + LicenseServices[i].ServiceName = pLicenseService->ServiceName; + LicenseServices[i].NumberLicenses = pLicenseService->NumberLicenses; + } + } + + *pLicenseServices = LicenseServices; + *pLicenseServiceTableSize = TotalRecords; + + ////////////////////////////////////////////////////////////////// + // + // Now Do Per Server License Service Table + // + TotalRecords = PerServerLicenseServiceListSize; + + // + // Make sure there is anything to replicate + // + if (TotalRecords > 0) + { + // + // Create our buffer to hold all of the garbage + // + PerServerLicenseServices = MIDL_user_allocate(TotalRecords * sizeof(PACK_LICENSE_SERVICE_RECORD)); + if (PerServerLicenseServices == NULL) + { + ASSERT(FALSE); + + // + // Clean up already alloc'd information + // + if (LicenseServices != NULL) + MIDL_user_free(LicenseServices); + + *pLicenseServices = NULL; + *pLicenseServiceTableSize = 0; + + return STATUS_NO_MEMORY; + } + + // + // Fill in the buffer - walk the Per Server License Services tree + // + for (i = 0; i < PerServerLicenseServiceListSize; i++) + { + pPerServerLicenseService = PerServerLicenseServiceList[i]; + + // + // Make index match table in it's current state + // + pPerServerLicenseService->Index = i; + + PerServerLicenseServices[i].ServiceName = pPerServerLicenseService->ServiceName; + PerServerLicenseServices[i].NumberLicenses = pPerServerLicenseService->NumberLicenses; + } + } + + *pPerServerLicenseServices = PerServerLicenseServices; + *pPerServerLicenseServiceTableSize = TotalRecords; + + ////////////////////////////////////////////////////////////////// + // + // Now Do License Purchase Records + // + TotalRecords = PurchaseListSize; + + // + // Make sure there is anything to replicate + // + if (TotalRecords > 0) { + // + // Create our buffer to hold all of the garbage + // + Licenses = MIDL_user_allocate(TotalRecords * sizeof(PACK_LICENSE_PURCHASE_RECORD)); + if (Licenses == NULL) { + ASSERT(FALSE); + + // + // Clean up already alloc'd information + // + if (LicenseServices != NULL) + MIDL_user_free(LicenseServices); + if (PerServerLicenseServices != NULL) + MIDL_user_free(PerServerLicenseServices); + + *pLicenseServices = NULL; + *pLicenseServiceTableSize = 0; + *pPerServerLicenseServices = NULL; + *pPerServerLicenseServiceTableSize = 0; + + return STATUS_NO_MEMORY; + } + + // + // Fill in the buffer - walk the License Purchase tree + // + for (i = 0; i < PurchaseListSize; i++) { + pLicense = &PurchaseList[i]; + + // + // License Service table index is fixed-up to what we need + // + Licenses[i].Service = ( pLicense->AllowedModes & 1 ) ? pLicense->Service->Index + : 0xFFFFFFFF; + Licenses[i].NumberLicenses = pLicense->NumberLicenses; + Licenses[i].Date = pLicense->Date; + Licenses[i].Admin = pLicense->Admin; + Licenses[i].Comment = pLicense->Comment; + + Licenses[i].PerServerService = ( pLicense->AllowedModes & 2 ) ? pLicense->PerServerService->Index + : 0xFFFFFFFF; + Licenses[i].AllowedModes = pLicense->AllowedModes; + Licenses[i].CertificateID = pLicense->CertificateID; + Licenses[i].Source = pLicense->Source; + Licenses[i].ExpirationDate = pLicense->ExpirationDate; + Licenses[i].MaxQuantity = pLicense->MaxQuantity; + Licenses[i].Vendor = pLicense->Vendor; + memcpy( Licenses[i].Secrets, pLicense->Secrets, LLS_NUM_SECRETS * sizeof( *pLicense->Secrets ) ); + } + } + + *pLicenses = Licenses; + *pLicenseTableSize = TotalRecords; + return Status; +} // LicenseListPack + + +///////////////////////////////////////////////////////////////////////// +VOID +LicenseListUnpack ( + ULONG LicenseServiceTableSize, + PPACK_LICENSE_SERVICE_RECORD LicenseServices, + + ULONG LicenseTableSize, + PPACK_LICENSE_PURCHASE_RECORD Licenses, + + ULONG PerServerLicenseServiceTableSize, + PPACK_LICENSE_SERVICE_RECORD PerServerLicenseServices + ) + +/*++ + +Routine Description: + + +Arguments: + +Return Value: + + +--*/ + +{ + NTSTATUS Status = STATUS_SUCCESS; + ULONG i; + PPACK_LICENSE_PURCHASE_RECORD pLicense; + LPTSTR ServiceName; + +#if DBG + if (TraceFlags & TRACE_FUNCTION_TRACE) + dprintf(TEXT("LicenseListUnpack: Service[%lu] PerServerService[%lu] License[%lu]\n"), LicenseServiceTableSize, PerServerLicenseServiceTableSize, LicenseTableSize); +#endif + + // + // Walk services table, adding any new services to our local table. + // Fix up the index pointers to match our local services. + // + RtlAcquireResourceExclusive(&LicenseListLock, TRUE); + + for (i = 0; i < LicenseTableSize; i++) + { + pLicense = &Licenses[i]; + + ServiceName = NULL; + + if ( pLicense->AllowedModes & LLS_LICENSE_MODE_ALLOW_PER_SERVER ) + { + if ( pLicense->PerServerService < PerServerLicenseServiceTableSize ) + { + ServiceName = PerServerLicenseServices[ pLicense->PerServerService ].ServiceName; + } + else + { + ASSERT( FALSE ); + } + } + + if ( ( NULL == ServiceName ) && ( pLicense->AllowedModes & LLS_LICENSE_MODE_ALLOW_PER_SEAT ) ) + { + if ( pLicense->Service < LicenseServiceTableSize ) + { + ServiceName = LicenseServices[ pLicense->Service ].ServiceName; + } + else + { + ASSERT( FALSE ); + } + } + + if ( NULL == ServiceName ) + { + ASSERT( FALSE ); + } + else + { + Status = LicenseAdd( ServiceName, pLicense->Vendor, pLicense->NumberLicenses, pLicense->MaxQuantity, pLicense->Admin, pLicense->Comment, pLicense->Date, pLicense->AllowedModes, pLicense->CertificateID, pLicense->Source, pLicense->ExpirationDate, pLicense->Secrets ); + + if (Status != STATUS_SUCCESS) + { +#ifdef DBG + dprintf(TEXT("LicenseAdd failed: 0x%lX\n"), Status); +#endif + // ASSERT(FALSE); + } + } + } + + RtlReleaseResource(&LicenseListLock); + +} // LicenseListUnpack + + +///////////////////////////////////////////////////////////////////////// +NTSTATUS +LicenseListStringsPack ( + ULONG LicenseServiceTableSize, + PPACK_LICENSE_SERVICE_RECORD LicenseServices, + + ULONG *pLicenseServiceStringSize, + LPTSTR *pLicenseServiceStrings, + + ULONG LicenseTableSize, + PPACK_LICENSE_PURCHASE_RECORD Licenses, + + ULONG *pLicenseStringSize, + LPTSTR *pLicenseStrings, + + ULONG PerServerLicenseServiceTableSize, + PPACK_LICENSE_SERVICE_RECORD PerServerLicenseServices, + + ULONG *pPerServerLicenseServiceStringSize, + LPTSTR *pPerServerLicenseServiceStrings + ) + +/*++ + +Routine Description: + + +Arguments: + +Return Value: + + +--*/ + +{ + NTSTATUS Status = STATUS_SUCCESS; + ULONG i; + ULONG StringSize; + PPACK_LICENSE_SERVICE_RECORD pSvc; + PPACK_LICENSE_PURCHASE_RECORD pLicense; + LPTSTR LicenseServiceStrings = NULL; + LPTSTR LicenseStrings = NULL; + TCHAR *pStr; + LPTSTR PerServerLicenseServiceStrings = NULL; + +#if DBG + if (TraceFlags & TRACE_FUNCTION_TRACE) + dprintf(TEXT("LicenseListStringsPack\n")); +#endif + + ASSERT(pLicenseServiceStrings != NULL); + ASSERT(pLicenseServiceStringSize != NULL); + + *pLicenseServiceStrings = NULL; + *pLicenseServiceStringSize = 0; + + ASSERT(pLicenseStrings != NULL); + ASSERT(pLicenseStringSize != NULL); + + *pLicenseStrings = NULL; + *pLicenseStringSize = 0; + + ASSERT(pPerServerLicenseServiceStrings != NULL); + ASSERT(pPerServerLicenseServiceStringSize != NULL); + + *pPerServerLicenseServiceStrings = NULL; + *pPerServerLicenseServiceStringSize = 0; + + ////////////////////////////////////////////////////////////////// + // + // Do License Service Strings + // + + // + // First walk the list adding up string sizes - to calculate our buff size + // + StringSize = 0; + for (i = 0; i < LicenseServiceTableSize; i++) { + pSvc = &LicenseServices[i]; + + StringSize = StringSize + lstrlen(pSvc->ServiceName) + 1; + } + + // + // Make sure there is anything to replicate + // + if (StringSize > 0) { + // + // Create our buffer to hold all of the garbage + // + LicenseServiceStrings = MIDL_user_allocate(StringSize * sizeof(TCHAR)); + if (LicenseServiceStrings == NULL) { + ASSERT(FALSE); + return STATUS_NO_MEMORY; + } + + // + // Fill in the buffer + // + pStr = LicenseServiceStrings; + for (i = 0; i < LicenseServiceTableSize; i++) { + pSvc = &LicenseServices[i]; + + lstrcpy(pStr, pSvc->ServiceName); + pStr = &pStr[lstrlen(pSvc->ServiceName) + 1]; + } + } + + *pLicenseServiceStrings = LicenseServiceStrings; + *pLicenseServiceStringSize = StringSize; + + ////////////////////////////////////////////////////////////////// + // + // Do Per Server License Service Strings + // + + // + // First walk the list adding up string sizes - to calculate our buff size + // + StringSize = 0; + for (i = 0; i < PerServerLicenseServiceTableSize; i++) { + pSvc = &PerServerLicenseServices[i]; + + StringSize = StringSize + lstrlen(pSvc->ServiceName) + 1; + } + + // + // Make sure there is anything to replicate + // + if (StringSize > 0) { + // + // Create our buffer to hold all of the garbage + // + PerServerLicenseServiceStrings = MIDL_user_allocate(StringSize * sizeof(TCHAR)); + if (PerServerLicenseServiceStrings == NULL) + { + ASSERT(FALSE); + + // + // Clean up already alloc'd information + // + if (LicenseServiceStrings != NULL) + MIDL_user_free(LicenseServiceStrings); + + *pLicenseServiceStrings = NULL; + *pLicenseServiceStringSize = 0; + + return STATUS_NO_MEMORY; + } + + // + // Fill in the buffer + // + pStr = PerServerLicenseServiceStrings; + for (i = 0; i < PerServerLicenseServiceTableSize; i++) + { + pSvc = &PerServerLicenseServices[i]; + + lstrcpy(pStr, pSvc->ServiceName); + pStr = &pStr[lstrlen(pSvc->ServiceName) + 1]; + } + } + + *pPerServerLicenseServiceStrings = PerServerLicenseServiceStrings; + *pPerServerLicenseServiceStringSize = StringSize; + + ////////////////////////////////////////////////////////////////// + // + // Now Do License Purchase Strings + // + + // + // First walk the list adding up string sizes - to calculate our buff size + // + StringSize = 0; + for (i = 0; i < LicenseTableSize; i++) { + pLicense = &Licenses[i]; + + StringSize = StringSize + lstrlen(pLicense->Vendor) + 1; + StringSize = StringSize + lstrlen(pLicense->Admin) + 1; + StringSize = StringSize + lstrlen(pLicense->Comment) + 1; + StringSize = StringSize + lstrlen(pLicense->Source) + 1; + } + + // + // Make sure there is anything to replicate + // + if (StringSize > 0) { + // + // Create our buffer to hold all of the garbage + // + LicenseStrings = MIDL_user_allocate(StringSize * sizeof(TCHAR)); + if (LicenseStrings == NULL) { + ASSERT(FALSE); + + // + // Clean up already alloc'd information + // + if (LicenseServiceStrings != NULL) + MIDL_user_free(LicenseServiceStrings); + if (PerServerLicenseServiceStrings != NULL) + MIDL_user_free(PerServerLicenseServiceStrings); + + *pLicenseServiceStrings = NULL; + *pLicenseServiceStringSize = 0; + *pPerServerLicenseServiceStrings = NULL; + *pPerServerLicenseServiceStringSize = 0; + + return STATUS_NO_MEMORY; + } + + // + // Fill in the buffer + // + pStr = LicenseStrings; + for (i = 0; i < LicenseTableSize; i++) { + pLicense = &Licenses[i]; + + lstrcpy(pStr, pLicense->Vendor); + pStr = &pStr[lstrlen(pLicense->Vendor) + 1]; + + lstrcpy(pStr, pLicense->Admin); + pStr = &pStr[lstrlen(pLicense->Admin) + 1]; + + lstrcpy(pStr, pLicense->Comment); + pStr = &pStr[lstrlen(pLicense->Comment) + 1]; + + lstrcpy(pStr, pLicense->Source); + pStr = &pStr[lstrlen(pLicense->Source) + 1]; + } + } + + *pLicenseStrings = LicenseStrings; + *pLicenseStringSize = StringSize; + + return Status; +} // LicenseListStringsPack + + +///////////////////////////////////////////////////////////////////////// +VOID +LicenseListStringsUnpack ( + ULONG LicenseServiceTableSize, + PPACK_LICENSE_SERVICE_RECORD LicenseServices, + + ULONG LicenseServiceStringSize, + LPTSTR LicenseServiceStrings, + + ULONG LicenseTableSize, + PPACK_LICENSE_PURCHASE_RECORD Licenses, + + ULONG LicenseStringSize, + LPTSTR LicenseStrings, + + ULONG PerServerLicenseServiceTableSize, + PPACK_LICENSE_SERVICE_RECORD PerServerLicenseServices, + + ULONG PerServerLicenseServiceStringSize, + LPTSTR PerServerLicenseServiceStrings + ) + +/*++ + +Routine Description: + + +Arguments: + +Return Value: + + +--*/ + +{ + ULONG i; + PPACK_LICENSE_SERVICE_RECORD pSvc; + PPACK_LICENSE_PURCHASE_RECORD pLicense; + TCHAR *pStr; + +#if DBG + if (TraceFlags & TRACE_FUNCTION_TRACE) + dprintf(TEXT("LicenseListStringsUnpack\n")); +#endif + + // + // First do per seat license service strings + // + pStr = LicenseServiceStrings; + for (i = 0; i < LicenseServiceTableSize; i++) { + pSvc = &LicenseServices[i]; + + pSvc->ServiceName = pStr; + + // + // Move to end of current string + // + while (*pStr != TEXT('\0')) + pStr++; + + // now go past ending NULL + pStr++; + } + + // + // Then do per server license service strings + // + pStr = PerServerLicenseServiceStrings; + for (i = 0; i < PerServerLicenseServiceTableSize; i++) { + pSvc = &PerServerLicenseServices[i]; + + pSvc->ServiceName = pStr; + + // + // Move to end of current string + // + while (*pStr != TEXT('\0')) + pStr++; + + // now go past ending NULL + pStr++; + } + + // + // Now do license purchase strings + // + pStr = LicenseStrings; + for (i = 0; i < LicenseTableSize; i++) { + pLicense = &Licenses[i]; + + pLicense->Vendor = pStr; + + // + // Move to end of current string + // + while (*pStr != TEXT('\0')) + pStr++; + + // now go past ending NULL + pStr++; + + pLicense->Admin = pStr; + + // + // Move to end of current string + // + while (*pStr != TEXT('\0')) + pStr++; + + // now go past ending NULL + pStr++; + + pLicense->Comment = pStr; + + // + // Move to end of current string + // + while (*pStr != TEXT('\0')) + pStr++; + + // now go past ending NULL + pStr++; + + pLicense->Source = pStr; + + // + // Move to end of current string + // + while (*pStr != TEXT('\0')) + pStr++; + + // now go past ending NULL + pStr++; + } + +} // LicenseListStringsUnpack + + +///////////////////////////////////////////////////////////////////////// +VOID +LicenseListLoad() + +/*++ + +Routine Description: + +Arguments: + + None. + +Return Value: + + None. + +--*/ + +{ + BOOL ret; + DWORD Version, DataSize; + NTSTATUS Status = STATUS_SUCCESS; + HANDLE hFile = NULL; + + ULONG LicenseServiceTableSize; + PPACK_LICENSE_SERVICE_RECORD LicenseServices = NULL; + + ULONG LicenseServiceStringSize; + LPTSTR LicenseServiceStrings = NULL; + + ULONG PerServerLicenseServiceTableSize; + PPACK_LICENSE_SERVICE_RECORD PerServerLicenseServices = NULL; + + ULONG PerServerLicenseServiceStringSize; + LPTSTR PerServerLicenseServiceStrings = NULL; + + ULONG LicenseTableSize; + PPACK_LICENSE_PURCHASE_RECORD Licenses = NULL; + + ULONG LicenseStringSize; + LPTSTR LicenseStrings = NULL; + + LICENSE_FILE_HEADER FileHeader; + DWORD BytesRead; + +#if DBG + if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_DATABASE)) + dprintf(TEXT("LLS TRACE: LicenseListLoad\n")); +#endif + + // + // Check if we already have file open + // + if (PurchaseFile != NULL) { + CloseHandle(PurchaseFile); + PurchaseFile = NULL; + } + + // + // If nothing to load then get-out + // + if (!FileExists(LicenseFileName)) + goto LicenseListLoadExit; + + // + // Check the init header + // + Version = DataSize = 0; + PurchaseFile = LlsFileCheck(LicenseFileName, &Version, &DataSize ); + if (PurchaseFile == NULL) { + Status = GetLastError(); + goto LicenseListLoadExit; + } + + if ( ( Version == LICENSE_FILE_VERSION_0 ) && ( DataSize == sizeof(LICENSE_FILE_HEADER_0) ) ) { + CloseHandle(PurchaseFile); + PurchaseFile = NULL; + + LicenseListLoadOld(); + return; + } + + if ( ( Version != LICENSE_FILE_VERSION ) || ( DataSize != sizeof(LICENSE_FILE_HEADER) ) ) { + Status = STATUS_FILE_INVALID; + goto LicenseListLoadExit; + } + + // + // The init header checks out, so load the license header and data blocks + // + hFile = PurchaseFile; + ret = ReadFile(hFile, &FileHeader, sizeof(LICENSE_FILE_HEADER), &BytesRead, NULL); + + LicenseServiceTableSize = 0; + LicenseServiceStringSize = 0; + LicenseTableSize = 0; + LicenseStringSize = 0; + + if (ret) { + // + // Run through and allocate space to read data blocks into + // + if (FileHeader.LicenseServiceTableSize != 0) { + LicenseServiceTableSize = FileHeader.LicenseServiceTableSize / sizeof(PACK_LICENSE_SERVICE_RECORD); + LicenseServices = MIDL_user_allocate(FileHeader.LicenseServiceTableSize); + + if ( LicenseServices == NULL ) { + Status = STATUS_NO_MEMORY; + goto LicenseListLoadExit; + } + } + + if (FileHeader.LicenseServiceStringSize != 0) { + LicenseServiceStringSize = FileHeader.LicenseServiceStringSize / sizeof(TCHAR); + LicenseServiceStrings = MIDL_user_allocate(FileHeader.LicenseServiceStringSize); + + if ( LicenseServiceStrings == NULL ) { + Status = STATUS_NO_MEMORY; + goto LicenseListLoadExit; + } + } + + if (FileHeader.LicenseServiceTableSize != 0) { + PerServerLicenseServiceTableSize = FileHeader.PerServerLicenseServiceTableSize / sizeof(PACK_LICENSE_SERVICE_RECORD); + PerServerLicenseServices = MIDL_user_allocate(FileHeader.PerServerLicenseServiceTableSize); + + if ( PerServerLicenseServices == NULL ) { + Status = STATUS_NO_MEMORY; + goto LicenseListLoadExit; + } + } + + if (FileHeader.PerServerLicenseServiceStringSize != 0) { + PerServerLicenseServiceStringSize = FileHeader.PerServerLicenseServiceStringSize / sizeof(TCHAR); + PerServerLicenseServiceStrings = MIDL_user_allocate(FileHeader.PerServerLicenseServiceStringSize); + + if ( PerServerLicenseServiceStrings == NULL ) { + Status = STATUS_NO_MEMORY; + goto LicenseListLoadExit; + } + } + + if (FileHeader.LicenseTableSize != 0) { + LicenseTableSize = FileHeader.LicenseTableSize / sizeof(PACK_LICENSE_PURCHASE_RECORD); + Licenses = MIDL_user_allocate(FileHeader.LicenseTableSize); + + if ( Licenses == NULL ) { + Status = STATUS_NO_MEMORY; + goto LicenseListLoadExit; + } + } + + if (FileHeader.LicenseStringSize != 0) { + LicenseStringSize = FileHeader.LicenseStringSize / sizeof(TCHAR); + LicenseStrings = MIDL_user_allocate(FileHeader.LicenseStringSize); + + if ( LicenseStrings == NULL ) { + Status = STATUS_NO_MEMORY; + goto LicenseListLoadExit; + } + } + + } + + if (ret && (FileHeader.LicenseServiceTableSize != 0) ) + ret = ReadFile(hFile, LicenseServices, FileHeader.LicenseServiceTableSize, &BytesRead, NULL); + + if (ret && (FileHeader.LicenseServiceStringSize != 0) ) + ret = ReadFile(hFile, LicenseServiceStrings, FileHeader.LicenseServiceStringSize, &BytesRead, NULL); + + if (ret && (FileHeader.PerServerLicenseServiceTableSize != 0) ) + ret = ReadFile(hFile, PerServerLicenseServices, FileHeader.PerServerLicenseServiceTableSize, &BytesRead, NULL); + + if (ret && (FileHeader.PerServerLicenseServiceStringSize != 0) ) + ret = ReadFile(hFile, PerServerLicenseServiceStrings, FileHeader.PerServerLicenseServiceStringSize, &BytesRead, NULL); + + if (ret && (FileHeader.LicenseTableSize != 0) ) + ret = ReadFile(hFile, Licenses, FileHeader.LicenseTableSize, &BytesRead, NULL); + + if (ret && (FileHeader.LicenseStringSize != 0) ) + ret = ReadFile(hFile, LicenseStrings, FileHeader.LicenseStringSize, &BytesRead, NULL); + + if (!ret) { + Status = GetLastError(); + goto LicenseListLoadExit; + } + + // + // Decrypt the data + // + Status = DeBlock(LicenseServices, FileHeader.LicenseServiceTableSize); + + if (Status == STATUS_SUCCESS) + Status = DeBlock(LicenseServiceStrings, FileHeader.LicenseServiceStringSize); + + if (Status == STATUS_SUCCESS) + Status = DeBlock(PerServerLicenseServices, FileHeader.PerServerLicenseServiceTableSize); + + if (Status == STATUS_SUCCESS) + Status = DeBlock(PerServerLicenseServiceStrings, FileHeader.PerServerLicenseServiceStringSize); + + if (Status == STATUS_SUCCESS) + Status = DeBlock(Licenses, FileHeader.LicenseTableSize); + + if (Status == STATUS_SUCCESS) + Status = DeBlock(LicenseStrings, FileHeader.LicenseStringSize); + + if (Status != STATUS_SUCCESS) + goto LicenseListLoadExit; + + + // + // Unpack the string data + // + LicenseListStringsUnpack( LicenseServiceTableSize, LicenseServices, + LicenseServiceStringSize, LicenseServiceStrings, + LicenseTableSize, Licenses, + LicenseStringSize, LicenseStrings, + PerServerLicenseServiceTableSize, PerServerLicenseServices, + PerServerLicenseServiceStringSize, PerServerLicenseServiceStrings + ); + + // + // Unpack the license data + // + LicenseListUnpack( LicenseServiceTableSize, LicenseServices, LicenseTableSize, Licenses, PerServerLicenseServiceTableSize, PerServerLicenseServices ); + +LicenseListLoadExit: + + // Note: Don't close the License Purchase File (keep it locked). + + // + // Run through our tables and clean them up + // + if (LicenseServices != NULL) + MIDL_user_free(LicenseServices); + + if (LicenseServiceStrings != NULL) + MIDL_user_free(LicenseServiceStrings); + + if (PerServerLicenseServices != NULL) + MIDL_user_free(PerServerLicenseServices); + + if (PerServerLicenseServiceStrings != NULL) + MIDL_user_free(PerServerLicenseServiceStrings); + + if (Licenses != NULL) + MIDL_user_free(Licenses); + + if (LicenseStrings != NULL) + MIDL_user_free(LicenseStrings); + + // + // If there was an error log it. + // + if (Status != STATUS_SUCCESS) + LogEvent(LLS_EVENT_LOAD_LICENSE, 0, NULL, Status); + +} // LicenseListLoad + + +///////////////////////////////////////////////////////////////////////// +NTSTATUS +LicenseListSave() + +/*++ + +Routine Description: + +Arguments: + + None. + +Return Value: + + None. + +--*/ + +{ + BOOL ret = TRUE; + NTSTATUS Status = STATUS_SUCCESS; + HANDLE hFile = NULL; + + ULONG LicenseServiceTableSize; + PPACK_LICENSE_SERVICE_RECORD LicenseServices = NULL; + + ULONG LicenseServiceStringSize; + LPTSTR LicenseServiceStrings = NULL; + + ULONG PerServerLicenseServiceTableSize; + PPACK_LICENSE_SERVICE_RECORD PerServerLicenseServices = NULL; + + ULONG PerServerLicenseServiceStringSize; + LPTSTR PerServerLicenseServiceStrings = NULL; + + ULONG LicenseTableSize; + PPACK_LICENSE_PURCHASE_RECORD Licenses = NULL; + + ULONG LicenseStringSize; + LPTSTR LicenseStrings = NULL; + + LICENSE_FILE_HEADER FileHeader; + DWORD BytesWritten; + +#if DBG + if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_DATABASE)) + dprintf(TEXT("LLS TRACE: LicenseListSave\n")); +#endif + + // + // Check if we already have file open + // + if (PurchaseFile != NULL) { + CloseHandle(PurchaseFile); + PurchaseFile = NULL; + } + + RtlAcquireResourceExclusive(&LicenseListLock, TRUE); + + // + // If nothing to save then get-out + // + if ( (LicenseServiceListSize == 0) && (PerServerLicenseServiceListSize == 0) ) + goto LicenseListSaveExit; + + // + // Pack the license data + // + Status = LicenseListPack( &LicenseServiceTableSize, &LicenseServices, &LicenseTableSize, &Licenses, &PerServerLicenseServiceTableSize, &PerServerLicenseServices ); + if (Status != STATUS_SUCCESS) + goto LicenseListSaveExit; + + // + // Now pack the String data + // + Status = LicenseListStringsPack( LicenseServiceTableSize, LicenseServices, + &LicenseServiceStringSize, &LicenseServiceStrings, + LicenseTableSize, Licenses, + &LicenseStringSize, &LicenseStrings, + PerServerLicenseServiceTableSize, PerServerLicenseServices, + &PerServerLicenseServiceStringSize, &PerServerLicenseServiceStrings ); + + if (Status != STATUS_SUCCESS) + goto LicenseListSaveExit; + + // + // Fill out the file header - sizes are byte sizes + // + FileHeader.LicenseServiceTableSize = LicenseServiceTableSize * sizeof(PACK_LICENSE_SERVICE_RECORD); + FileHeader.LicenseServiceStringSize = LicenseServiceStringSize * sizeof(TCHAR); + FileHeader.PerServerLicenseServiceTableSize = PerServerLicenseServiceTableSize * sizeof(PACK_LICENSE_SERVICE_RECORD); + FileHeader.PerServerLicenseServiceStringSize = PerServerLicenseServiceStringSize * sizeof(TCHAR); + FileHeader.LicenseTableSize = LicenseTableSize * sizeof(PACK_LICENSE_PURCHASE_RECORD); + FileHeader.LicenseStringSize = LicenseStringSize * sizeof(TCHAR); + + // + // Encrypt the data before saving it out. + // + Status = EBlock(LicenseServices, FileHeader.LicenseServiceTableSize); + + if (Status == STATUS_SUCCESS) + Status = EBlock(LicenseServiceStrings, FileHeader.LicenseServiceStringSize); + + if (Status == STATUS_SUCCESS) + Status = EBlock(PerServerLicenseServices, FileHeader.PerServerLicenseServiceTableSize); + + if (Status == STATUS_SUCCESS) + Status = EBlock(PerServerLicenseServiceStrings, FileHeader.PerServerLicenseServiceStringSize); + + if (Status == STATUS_SUCCESS) + Status = EBlock(Licenses, FileHeader.LicenseTableSize); + + if (Status == STATUS_SUCCESS) + Status = EBlock(LicenseStrings, FileHeader.LicenseStringSize); + + if (Status != STATUS_SUCCESS) + goto LicenseListSaveExit; + + // + // Save out the header record + // + PurchaseFile = LlsFileInit(LicenseFileName, LICENSE_FILE_VERSION, sizeof(LICENSE_FILE_HEADER) ); + if (PurchaseFile == NULL) { + Status = GetLastError(); + goto LicenseListSaveExit; + } + + // + // Now write out all the data blocks + // + hFile = PurchaseFile; + + ret = WriteFile(hFile, &FileHeader, sizeof(LICENSE_FILE_HEADER), &BytesWritten, NULL); + + if (ret && (LicenseServices != NULL) && (FileHeader.LicenseServiceTableSize != 0)) + ret = WriteFile(hFile, LicenseServices, FileHeader.LicenseServiceTableSize, &BytesWritten, NULL); + + if (ret && (LicenseServiceStrings != NULL) && (FileHeader.LicenseServiceStringSize != 0)) + ret = WriteFile(hFile, LicenseServiceStrings, FileHeader.LicenseServiceStringSize, &BytesWritten, NULL); + + if (ret && (PerServerLicenseServices != NULL) && (FileHeader.PerServerLicenseServiceTableSize != 0)) + ret = WriteFile(hFile, PerServerLicenseServices, FileHeader.PerServerLicenseServiceTableSize, &BytesWritten, NULL); + + if (ret && (PerServerLicenseServiceStrings != NULL) && (FileHeader.PerServerLicenseServiceStringSize != 0)) + ret = WriteFile(hFile, PerServerLicenseServiceStrings, FileHeader.PerServerLicenseServiceStringSize, &BytesWritten, NULL); + + if (ret && (Licenses != NULL) && (FileHeader.LicenseTableSize != 0)) + ret = WriteFile(hFile, Licenses, FileHeader.LicenseTableSize, &BytesWritten, NULL); + + if (ret && (LicenseStrings != NULL) && (FileHeader.LicenseStringSize != 0)) + ret = WriteFile(hFile, LicenseStrings, FileHeader.LicenseStringSize, &BytesWritten, NULL); + + if (!ret) + Status = GetLastError(); + +LicenseListSaveExit: + RtlReleaseResource(&LicenseListLock); + + // Note: Don't close the License Purchase File (keep it locked). + if (hFile != NULL) + FlushFileBuffers(hFile); + + // + // Run through our tables and clean them up + // + if (LicenseServices != NULL) + MIDL_user_free(LicenseServices); + + if (LicenseServiceStrings != NULL) + MIDL_user_free(LicenseServiceStrings); + + if (PerServerLicenseServices != NULL) + MIDL_user_free(PerServerLicenseServices); + + if (PerServerLicenseServiceStrings != NULL) + MIDL_user_free(PerServerLicenseServiceStrings); + + if (Licenses != NULL) + MIDL_user_free(Licenses); + + if (LicenseStrings != NULL) + MIDL_user_free(LicenseStrings); + + // + // If there was an error log it. + // + if (Status != STATUS_SUCCESS) + LogEvent(LLS_EVENT_SAVE_LICENSE, 0, NULL, Status); + + return Status; +} // LicenseListSave + + + +///////////////////////////////////////////////////////////////////////// +// Mapping List +// + +///////////////////////////////////////////////////////////////////////// +NTSTATUS +MappingListPack ( + ULONG *pMappingUserTableSize, + PPACK_MAPPING_USER_RECORD *pMappingUsers, + + ULONG *pMappingTableSize, + PPACK_MAPPING_RECORD *pMappings + ) + +/*++ + +Routine Description: + + +Arguments: + +Return Value: + + +--*/ + +{ + NTSTATUS Status = STATUS_SUCCESS; + PPACK_MAPPING_USER_RECORD MappingUsers = NULL; + PPACK_MAPPING_RECORD Mappings = NULL; + ULONG i, j, k; + ULONG TotalRecords = 0; + PMAPPING_RECORD pMapping; + +#if DBG + if (TraceFlags & TRACE_FUNCTION_TRACE) + dprintf(TEXT("LLS TRACE: MappingListPack\n")); +#endif + + ASSERT(pMappingUsers != NULL); + ASSERT(pMappingUserTableSize != NULL); + + *pMappingUsers = NULL; + *pMappingUserTableSize = 0; + + ASSERT(pMappings != NULL); + ASSERT(pMappingTableSize != NULL); + + *pMappings = NULL; + *pMappingTableSize = 0; + + ////////////////////////////////////////////////////////////////// + // + // Do Mapping User Table First + // + TotalRecords = 0; + + // + // Make sure there is anything to replicate + // + for (i = 0; i < MappingListSize; i++) + TotalRecords += MappingList[i]->NumMembers; + + if (TotalRecords > 0) { + // + // Create our buffer to hold all of the garbage + // + MappingUsers = MIDL_user_allocate(TotalRecords * sizeof(PACK_MAPPING_USER_RECORD)); + if (MappingUsers == NULL) { + ASSERT(FALSE); + return STATUS_NO_MEMORY; + } + + // + // Fill in the buffer - walk the Mapping tree + // + k = 0; + for (i = 0; i < MappingListSize; i++) { + pMapping = MappingList[i]; + + for (j = 0; j < pMapping->NumMembers; j++) { + MappingUsers[k].Mapping = i; + MappingUsers[k].Name = pMapping->Members[j]; + k++; + } + } + } + + *pMappingUsers = MappingUsers; + *pMappingUserTableSize = TotalRecords; + + ////////////////////////////////////////////////////////////////// + // + // Now Do Mapping Records + // + TotalRecords = MappingListSize; + + // + // Make sure there is anything to replicate + // + if (TotalRecords > 0) { + // + // Create our buffer to hold all of the garbage + // + Mappings = MIDL_user_allocate(TotalRecords * sizeof(PACK_MAPPING_RECORD)); + if (Mappings == NULL) { + ASSERT(FALSE); + + // + // Clean up already alloc'd information + // + if (MappingUsers != NULL) + MIDL_user_free(MappingUsers); + + *pMappingUsers = NULL; + *pMappingUserTableSize = 0; + + return STATUS_NO_MEMORY; + } + + // + // Fill in the buffer - walk the License Purchase tree + // + for (i = 0; i < MappingListSize; i++) { + pMapping = MappingList[i]; + + Mappings[i].Name = pMapping->Name; + Mappings[i].Comment = pMapping->Comment; + Mappings[i].Licenses = pMapping->Licenses; + } + } + + *pMappings = Mappings; + *pMappingTableSize = TotalRecords; + return Status; +} // MappingListPack + + +///////////////////////////////////////////////////////////////////////// +VOID +MappingListUnpack ( + ULONG MappingUserTableSize, + PPACK_MAPPING_USER_RECORD MappingUsers, + + ULONG MappingTableSize, + PPACK_MAPPING_RECORD Mappings + ) + +/*++ + +Routine Description: + + +Arguments: + +Return Value: + + +--*/ + +{ + NTSTATUS Status; + ULONG i; + PPACK_MAPPING_USER_RECORD pUsr; + PPACK_MAPPING_RECORD pMapping; + PMAPPING_RECORD pMap; + +#if DBG + if (TraceFlags & TRACE_FUNCTION_TRACE) + dprintf(TEXT("MappingListUnpack: Mappings[%lu] TotalUsers[%lu]\n"), MappingTableSize, MappingUserTableSize); +#endif + + RtlAcquireResourceExclusive(&MappingListLock, TRUE); + + // + // Add the Mappings first + // + for (i = 0; i < MappingTableSize; i++) { + pMapping = &Mappings[i]; + + pMap = MappingListAdd(pMapping->Name, pMapping->Comment, pMapping->Licenses); + + ASSERT(pMap != NULL); + } + + // + // Now add the users to the mappings... + // + for (i = 0; i < MappingUserTableSize; i++) { + pUsr = &MappingUsers[i]; + + pMap = NULL; + if (pUsr->Mapping < MappingTableSize) + pMap = MappingUserListAdd(Mappings[pUsr->Mapping].Name, pUsr->Name); + +#if DBG + if (pMap == NULL) { + dprintf(TEXT("pMap: 0x%lX pUsr->Mapping: %lu MappingTableSize: %lu\n"), pMap, pUsr->Mapping, MappingTableSize); + ASSERT(FALSE); + } +#endif + } + + RtlReleaseResource(&MappingListLock); + +} // MappingListUnpack + + +///////////////////////////////////////////////////////////////////////// +NTSTATUS +MappingListStringsPack ( + ULONG MappingUserTableSize, + PPACK_MAPPING_USER_RECORD MappingUsers, + + ULONG *pMappingUserStringSize, + LPTSTR *pMappingUserStrings, + + ULONG MappingTableSize, + PPACK_MAPPING_RECORD Mappings, + + ULONG *pMappingStringSize, + LPTSTR *pMappingStrings + ) + +/*++ + +Routine Description: + + +Arguments: + +Return Value: + + +--*/ + +{ + NTSTATUS Status = STATUS_SUCCESS; + ULONG i; + ULONG StringSize; + PPACK_MAPPING_USER_RECORD pUsr; + PPACK_MAPPING_RECORD pMapping; + LPTSTR MappingUserStrings = NULL; + LPTSTR MappingStrings = NULL; + TCHAR *pStr; + +#if DBG + if (TraceFlags & TRACE_FUNCTION_TRACE) + dprintf(TEXT("MappingListStringsPack\n")); +#endif + + ASSERT(pMappingUserStrings != NULL); + ASSERT(pMappingUserStringSize != NULL); + + *pMappingUserStrings = NULL; + *pMappingUserStringSize = 0; + + ASSERT(pMappingStrings != NULL); + ASSERT(pMappingStringSize != NULL); + + *pMappingStrings = NULL; + *pMappingStringSize = 0; + + ////////////////////////////////////////////////////////////////// + // + // Do Mapping User Strings + // + + // + // First walk the list adding up string sizes - to calculate our buff size + // + StringSize = 0; + for (i = 0; i < MappingUserTableSize; i++) { + pUsr = &MappingUsers[i]; + + StringSize = StringSize + lstrlen(pUsr->Name) + 1; + } + + // + // Make sure there is anything to replicate + // + if (StringSize > 0) { + // + // Create our buffer to hold all of the garbage + // + MappingUserStrings = MIDL_user_allocate(StringSize * sizeof(TCHAR)); + if (MappingUserStrings == NULL) { + ASSERT(FALSE); + return STATUS_NO_MEMORY; + } + + // + // Fill in the buffer + // + pStr = MappingUserStrings; + for (i = 0; i < MappingUserTableSize; i++) { + pUsr = &MappingUsers[i]; + + lstrcpy(pStr, pUsr->Name); + pStr = &pStr[lstrlen(pUsr->Name) + 1]; + } + } + + *pMappingUserStrings = MappingUserStrings; + *pMappingUserStringSize = StringSize; + + ////////////////////////////////////////////////////////////////// + // + // Now Do Mapping Strings + // + + // + // First walk the list adding up string sizes - to calculate our buff size + // + StringSize = 0; + for (i = 0; i < MappingTableSize; i++) { + pMapping = &Mappings[i]; + + StringSize = StringSize + lstrlen(pMapping->Name) + 1; + StringSize = StringSize + lstrlen(pMapping->Comment) + 1; + } + + // + // Make sure there is anything to replicate + // + if (StringSize > 0) { + // + // Create our buffer to hold all of the garbage + // + MappingStrings = MIDL_user_allocate(StringSize * sizeof(TCHAR)); + if (MappingStrings == NULL) { + ASSERT(FALSE); + + // + // Clean up already alloc'd information + // + if (MappingUserStrings != NULL) + MIDL_user_free(MappingUserStrings); + + *pMappingUserStrings = NULL; + *pMappingUserStringSize = 0; + + return STATUS_NO_MEMORY; + } + + // + // Fill in the buffer + // + pStr = MappingStrings; + for (i = 0; i < MappingTableSize; i++) { + pMapping = &Mappings[i]; + + lstrcpy(pStr, pMapping->Name); + pStr = &pStr[lstrlen(pMapping->Name) + 1]; + + lstrcpy(pStr, pMapping->Comment); + pStr = &pStr[lstrlen(pMapping->Comment) + 1]; + } + } + + *pMappingStrings = MappingStrings; + *pMappingStringSize = StringSize; + + return Status; +} // MappingListStringsPack + + +///////////////////////////////////////////////////////////////////////// +VOID +MappingListStringsUnpack ( + ULONG MappingUserTableSize, + PPACK_MAPPING_USER_RECORD MappingUsers, + + ULONG MappingUserStringSize, + LPTSTR MappingUserStrings, + + ULONG MappingTableSize, + PPACK_MAPPING_RECORD Mappings, + + ULONG MappingStringSize, + LPTSTR MappingStrings + ) + +/*++ + +Routine Description: + + +Arguments: + +Return Value: + + +--*/ + +{ + ULONG i; + PPACK_MAPPING_USER_RECORD pUsr; + PPACK_MAPPING_RECORD pMapping; + TCHAR *pStr; + +#if DBG + if (TraceFlags & TRACE_FUNCTION_TRACE) + dprintf(TEXT("MappingListStringsUnpack\n")); +#endif + + // + // First do license service strings + // + pStr = MappingUserStrings; + for (i = 0; i < MappingUserTableSize; i++) { + pUsr = &MappingUsers[i]; + + pUsr->Name = pStr; + + // + // Move to end of current string + // + while (*pStr != TEXT('\0')) + pStr++; + + // now go past ending NULL + pStr++; + } + + // + // Now do license purchase strings + // + pStr = MappingStrings; + for (i = 0; i < MappingTableSize; i++) { + pMapping = &Mappings[i]; + + pMapping->Name = pStr; + + // + // Move to end of current string + // + while (*pStr != TEXT('\0')) + pStr++; + + // now go past ending NULL + pStr++; + + pMapping->Comment = pStr; + + // + // Move to end of current string + // + while (*pStr != TEXT('\0')) + pStr++; + + // now go past ending NULL + pStr++; + } + +} // MappingListStringsUnpack + + +///////////////////////////////////////////////////////////////////////// +VOID +MappingListLoad() + +/*++ + +Routine Description: + +Arguments: + + None. + +Return Value: + + None. + +--*/ + +{ + BOOL ret; + DWORD Version, DataSize; + NTSTATUS Status = STATUS_SUCCESS; + HANDLE hFile = NULL; + + ULONG MappingUserTableSize; + PPACK_MAPPING_USER_RECORD MappingUsers = NULL; + + ULONG MappingUserStringSize; + LPTSTR MappingUserStrings = NULL; + + ULONG MappingTableSize; + PPACK_MAPPING_RECORD Mappings = NULL; + + ULONG MappingStringSize; + LPTSTR MappingStrings = NULL; + + MAPPING_FILE_HEADER FileHeader; + DWORD BytesRead; + +#if DBG + if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_DATABASE)) + dprintf(TEXT("LLS TRACE: MappingListLoad\n")); +#endif + + // + // If nothing to load then get-out + // + if (!FileExists(MappingFileName)) + goto MappingListLoadExit; + + // + // Check the init header + // + Version = DataSize = 0; + hFile = LlsFileCheck(MappingFileName, &Version, &DataSize ); + if (hFile == NULL) { + Status = GetLastError(); + goto MappingListLoadExit; + } + + if ((Version != MAPPING_FILE_VERSION) || (DataSize != sizeof(MAPPING_FILE_HEADER))) { + Status = STATUS_FILE_INVALID; + goto MappingListLoadExit; + } + + // + // The init header checks out, so load the license header and data blocks + // + ret = ReadFile(hFile, &FileHeader, sizeof(MAPPING_FILE_HEADER), &BytesRead, NULL); + + MappingUserTableSize = 0; + MappingUserStringSize = 0; + MappingTableSize = 0; + MappingStringSize = 0; + + if (ret) { + // + // Run through and allocate space to read data blocks into + // + if (FileHeader.MappingUserTableSize != 0) { + MappingUserTableSize = FileHeader.MappingUserTableSize / sizeof(PACK_MAPPING_USER_RECORD); + MappingUsers = MIDL_user_allocate(FileHeader.MappingUserTableSize); + + if ( MappingUsers == NULL ) { + Status = STATUS_NO_MEMORY; + goto MappingListLoadExit; + } + } + + if (FileHeader.MappingUserStringSize != 0) { + MappingUserStringSize = FileHeader.MappingUserStringSize / sizeof(TCHAR); + MappingUserStrings = MIDL_user_allocate(FileHeader.MappingUserStringSize); + + if ( MappingUserStrings == NULL ) { + Status = STATUS_NO_MEMORY; + goto MappingListLoadExit; + } + } + + if (FileHeader.MappingTableSize != 0) { + MappingTableSize = FileHeader.MappingTableSize / sizeof(PACK_MAPPING_RECORD); + Mappings = MIDL_user_allocate(FileHeader.MappingTableSize); + + if ( Mappings == NULL ) { + Status = STATUS_NO_MEMORY; + goto MappingListLoadExit; + } + } + + if (FileHeader.MappingStringSize != 0) { + MappingStringSize = FileHeader.MappingStringSize / sizeof(TCHAR); + MappingStrings = MIDL_user_allocate(FileHeader.MappingStringSize); + + if ( MappingStrings == NULL ) { + Status = STATUS_NO_MEMORY; + goto MappingListLoadExit; + } + } + + } + + if (ret && (FileHeader.MappingUserTableSize != 0) ) + ret = ReadFile(hFile, MappingUsers, FileHeader.MappingUserTableSize, &BytesRead, NULL); + + if (ret && (FileHeader.MappingUserStringSize != 0) ) + ret = ReadFile(hFile, MappingUserStrings, FileHeader.MappingUserStringSize, &BytesRead, NULL); + + if (ret && (FileHeader.MappingTableSize != 0) ) + ret = ReadFile(hFile, Mappings, FileHeader.MappingTableSize, &BytesRead, NULL); + + if (ret && (FileHeader.MappingStringSize != 0) ) + ret = ReadFile(hFile, MappingStrings, FileHeader.MappingStringSize, &BytesRead, NULL); + + if (!ret) { + Status = GetLastError(); + goto MappingListLoadExit; + } + + // + // Decrypt the data + // + Status = DeBlock(MappingUsers, FileHeader.MappingUserTableSize); + + if (Status == STATUS_SUCCESS) + Status = DeBlock(MappingUserStrings, FileHeader.MappingUserStringSize); + + if (Status == STATUS_SUCCESS) + Status = DeBlock(Mappings, FileHeader.MappingTableSize); + + if (Status == STATUS_SUCCESS) + Status = DeBlock(MappingStrings, FileHeader.MappingStringSize); + + if (Status != STATUS_SUCCESS) + goto MappingListLoadExit; + + + // + // Unpack the string data + // + MappingListStringsUnpack( MappingUserTableSize, MappingUsers, + MappingUserStringSize, MappingUserStrings, + MappingTableSize, Mappings, + MappingStringSize, MappingStrings ); + + // + // Unpack the data + // + MappingListUnpack( MappingUserTableSize, MappingUsers, MappingTableSize, Mappings ); + +MappingListLoadExit: + + if (hFile != NULL) + CloseHandle(hFile); + + // + // Run through our tables and clean them up + // + if (MappingUsers != NULL) + MIDL_user_free(MappingUsers); + + if (MappingUserStrings != NULL) + MIDL_user_free(MappingUserStrings); + + if (Mappings != NULL) + MIDL_user_free(Mappings); + + if (MappingStrings != NULL) + MIDL_user_free(MappingStrings); + + // + // If there was an error log it. + // + if (Status != STATUS_SUCCESS) + LogEvent(LLS_EVENT_LOAD_MAPPING, 0, NULL, Status); + +} // MappingListLoad + + +///////////////////////////////////////////////////////////////////////// +NTSTATUS +MappingListSave() + +/*++ + +Routine Description: + +Arguments: + + None. + +Return Value: + + None. + +--*/ + +{ + BOOL ret = TRUE; + NTSTATUS Status = STATUS_SUCCESS; + HANDLE hFile = NULL; + + ULONG MappingUserTableSize; + PPACK_MAPPING_USER_RECORD MappingUsers = NULL; + + ULONG MappingUserStringSize; + LPTSTR MappingUserStrings = NULL; + + ULONG MappingTableSize; + PPACK_MAPPING_RECORD Mappings = NULL; + + ULONG MappingStringSize; + LPTSTR MappingStrings = NULL; + + MAPPING_FILE_HEADER FileHeader; + DWORD BytesWritten; + +#if DBG + if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_DATABASE)) + dprintf(TEXT("LLS TRACE: MappingListSave\n")); +#endif + + RtlAcquireResourceExclusive(&MappingListLock, TRUE); + + // + // If nothing to save then get-out + // + if (MappingListSize == 0) + goto MappingListSaveExit; + + // + // Pack the data + // + Status = MappingListPack( &MappingUserTableSize, &MappingUsers, &MappingTableSize, &Mappings ); + if (Status != STATUS_SUCCESS) + goto MappingListSaveExit; + + // + // Now pack the String data + // + Status = MappingListStringsPack( MappingUserTableSize, MappingUsers, + &MappingUserStringSize, &MappingUserStrings, + MappingTableSize, Mappings, + &MappingStringSize, &MappingStrings ); + + if (Status != STATUS_SUCCESS) + goto MappingListSaveExit; + + // + // Fill out the file header - sizes are byte sizes + // + FileHeader.MappingUserTableSize = MappingUserTableSize * sizeof(PACK_MAPPING_USER_RECORD); + FileHeader.MappingUserStringSize = MappingUserStringSize * sizeof(TCHAR); + FileHeader.MappingTableSize = MappingTableSize * sizeof(PACK_MAPPING_RECORD); + FileHeader.MappingStringSize = MappingStringSize * sizeof(TCHAR); + + // + // Encrypt the data before saving it out. + // + Status = EBlock(MappingUsers, FileHeader.MappingUserTableSize); + + if (Status == STATUS_SUCCESS) + Status = EBlock(MappingUserStrings, FileHeader.MappingUserStringSize); + + if (Status == STATUS_SUCCESS) + Status = EBlock(Mappings, FileHeader.MappingTableSize); + + if (Status == STATUS_SUCCESS) + Status = EBlock(MappingStrings, FileHeader.MappingStringSize); + + if (Status != STATUS_SUCCESS) + goto MappingListSaveExit; + + // + // Save out the header record + // + hFile = LlsFileInit(MappingFileName, MAPPING_FILE_VERSION, sizeof(MAPPING_FILE_HEADER) ); + if (hFile == NULL) { + Status = GetLastError(); + goto MappingListSaveExit; + } + + // + // Now write out all the data blocks + // + ret = WriteFile(hFile, &FileHeader, sizeof(MAPPING_FILE_HEADER), &BytesWritten, NULL); + + if (ret && (MappingUsers != NULL) && (FileHeader.MappingUserTableSize != 0)) + ret = WriteFile(hFile, MappingUsers, FileHeader.MappingUserTableSize, &BytesWritten, NULL); + + if (ret && (MappingUserStrings != NULL) && (FileHeader.MappingUserStringSize != 0)) + ret = WriteFile(hFile, MappingUserStrings, FileHeader.MappingUserStringSize, &BytesWritten, NULL); + + if (ret && (Mappings != NULL) && (FileHeader.MappingTableSize != 0)) + ret = WriteFile(hFile, Mappings, FileHeader.MappingTableSize, &BytesWritten, NULL); + + if (ret && (MappingStrings != NULL) && (FileHeader.MappingStringSize != 0)) + ret = WriteFile(hFile, MappingStrings, FileHeader.MappingStringSize, &BytesWritten, NULL); + + if (!ret) + Status = GetLastError(); + +MappingListSaveExit: + RtlReleaseResource(&MappingListLock); + + if (hFile != NULL) + CloseHandle(hFile); + + // + // Run through our tables and clean them up + // + if (MappingUsers != NULL) + MIDL_user_free(MappingUsers); + + if (MappingUserStrings != NULL) + MIDL_user_free(MappingUserStrings); + + if (Mappings != NULL) + MIDL_user_free(Mappings); + + if (MappingStrings != NULL) + MIDL_user_free(MappingStrings); + + // + // If there was an error log it. + // + if (Status != STATUS_SUCCESS) + LogEvent(LLS_EVENT_SAVE_MAPPING, 0, NULL, Status); + + return Status; +} // MappingListSave + + + +///////////////////////////////////////////////////////////////////////// +// User List +// + +///////////////////////////////////////////////////////////////////////// +NTSTATUS +UserListPack ( + DWORD LastReplicated, + ULONG UserLevel, + ULONG *pUserTableSize, + LPVOID *pUsers + ) + +/*++ + +Routine Description: + + +Arguments: + +Return Value: + + +--*/ + +{ + NTSTATUS Status = STATUS_SUCCESS; + LPVOID Users = NULL; + ULONG i, j, k; + ULONG TotalRecords = 0; + PUSER_RECORD pUser; + +#if DBG + if (TraceFlags & TRACE_FUNCTION_TRACE) + dprintf(TEXT("LLS TRACE: UserListPack\n")); +#endif + + ASSERT(pUsers != NULL); + ASSERT(pUserTableSize != NULL); + + *pUsers = NULL; + *pUserTableSize = 0; + + // + // Now walk our tree and figure out how many records we must send. + // + i = 0; + TotalRecords = 0; + while (i < UserListNumEntries) { + pUser = RtlGetElementGenericTable(&UserList, i); + + if (pUser != NULL) { + // + // Walk each service under each user + // + RtlEnterCriticalSection(&pUser->ServiceTableLock); + + for (j = 0; j < pUser->ServiceTableSize; j++) + if ( (pUser->Services[j].AccessCount > 0) || (pUser->Services[j].LastAccess > LastReplicated) ) + TotalRecords++; + + RtlLeaveCriticalSection(&pUser->ServiceTableLock); + } + + i++; + } + +#if DBG + if (TraceFlags & TRACE_REPLICATION) + dprintf(TEXT(" LLS Packing %lu User Records\n"), TotalRecords); +#endif + + // + // Make sure there is anything to replicate + // + if (TotalRecords > 0) { + // + // Create our buffer to hold all of the garbage + // + Users = MIDL_user_allocate(TotalRecords * ( UserLevel ? sizeof(REPL_USER_RECORD_1) + : sizeof(REPL_USER_RECORD_0) ) ); + if (Users == NULL) { + ASSERT(FALSE); + return STATUS_NO_MEMORY; + } + + // + // Fill in the buffer - walk the user tree + // + i = 0; + j = 0; + while ((i < UserListNumEntries) && (j < TotalRecords)) { + pUser = RtlGetElementGenericTable(&UserList, i); + + if (pUser != NULL) { + // + // Walk each service under each user + // + k = 0; + RtlEnterCriticalSection(&pUser->ServiceTableLock); + while (k < pUser->ServiceTableSize) { + if ( (pUser->Services[k].AccessCount > 0) || (pUser->Services[k].LastAccess > LastReplicated) ) { + if ( 0 == UserLevel ) + { + ((PREPL_USER_RECORD_0)Users)[j].Name = pUser->UserID; + ((PREPL_USER_RECORD_0)Users)[j].Service = pUser->Services[k].Service->Index; + ((PREPL_USER_RECORD_0)Users)[j].AccessCount = pUser->Services[k].AccessCount; + ((PREPL_USER_RECORD_0)Users)[j].LastAccess = pUser->Services[k].LastAccess; + } + else + { + ((PREPL_USER_RECORD_1)Users)[j].Name = pUser->UserID; + ((PREPL_USER_RECORD_1)Users)[j].Service = pUser->Services[k].Service->Index; + ((PREPL_USER_RECORD_1)Users)[j].AccessCount = pUser->Services[k].AccessCount; + ((PREPL_USER_RECORD_1)Users)[j].LastAccess = pUser->Services[k].LastAccess; + ((PREPL_USER_RECORD_1)Users)[j].Flags = pUser->Flags; + } + + // + // Reset access count so we don't increment forever + // + if (LastReplicated != 0) + pUser->Services[k].AccessCount = 0; + + j++; + } + + k++; + } + RtlLeaveCriticalSection(&pUser->ServiceTableLock); + } + + i++; + } + } // User Records + +#if DBG + if (TraceFlags & TRACE_REPLICATION) + dprintf(TEXT("UserListPack: [%lu]\n"), TotalRecords); +#endif + *pUsers = Users; + *pUserTableSize = TotalRecords; + return Status; +} // UserListPack + + +///////////////////////////////////////////////////////////////////////// +VOID +UserListUnpack ( + ULONG ServiceTableSize, + PREPL_SERVICE_RECORD Services, + + ULONG ServerTableSize, + PREPL_SERVER_RECORD Servers, + + ULONG ServerServiceTableSize, + PREPL_SERVER_SERVICE_RECORD ServerServices, + + ULONG UserLevel, + ULONG UserTableSize, + LPVOID Users + ) + +/*++ + +Routine Description: + + +Arguments: + +Return Value: + + +--*/ + +{ + NTSTATUS Status = STATUS_SUCCESS; + ULONG i; + PUSER_RECORD pUser; + PREPL_USER_RECORD_0 pReplUser0 = NULL; + PREPL_USER_RECORD_1 pReplUser1 = NULL; + PADD_CACHE pAdd = NULL; + PADD_CACHE tAdd = NULL; + PADD_CACHE lAdd = NULL; + ULONG CacheSize = 0; + ULONG DataLength; + LPTSTR NewName; + +#if DBG + if (TraceFlags & (TRACE_REPLICATION | TRACE_FUNCTION_TRACE)) + dprintf(TEXT("UserListUnpack: [%lu]\n"), UserTableSize); +#endif + // + // Walk User table. First fixup service pointers to our local service + // table. Next create a big add cache list to dump onto our add-cache + // queue. + // + for (i = 0; i < UserTableSize; i++) { + // + // Update Index + // + if ( 0 == UserLevel ) + { + pReplUser0 = &( (PREPL_USER_RECORD_0) Users)[i]; + pReplUser0->Service = Services[pReplUser0->Service].Index; + } + else + { + pReplUser1 = &( (PREPL_USER_RECORD_1) Users)[i]; + pReplUser1->Service = Services[pReplUser1->Service].Index; + } + + // + // Now create Add Cache object + // + tAdd = LocalAlloc(LPTR, sizeof(ADD_CACHE)); + if (tAdd != NULL) { + if ( 0 == UserLevel ) + { + DataLength = (lstrlen(pReplUser0->Name) + 1) * sizeof(TCHAR); + } + else + { + DataLength = (lstrlen(pReplUser1->Name) + 1) * sizeof(TCHAR); + } + + NewName = LocalAlloc( LPTR, DataLength); + + if (NewName == NULL) { + LocalFree(pAdd); + ASSERT(FALSE); + } else { + tAdd->Data = NewName; + tAdd->DataType = DATA_TYPE_USERNAME; + tAdd->DataLength = DataLength; + + if ( 0 == UserLevel ) + { + lstrcpy( NewName, pReplUser0->Name ); + tAdd->AccessCount = pReplUser0->AccessCount; + tAdd->LastAccess = pReplUser0->LastAccess; + tAdd->Flags = LLS_FLAG_SUITE_AUTO; + + RtlAcquireResourceShared(&MasterServiceListLock, TRUE); + tAdd->Service = MasterServiceTable[pReplUser0->Service]; + RtlReleaseResource(&MasterServiceListLock); + } + else + { + lstrcpy( NewName, pReplUser1->Name ); + tAdd->AccessCount = pReplUser1->AccessCount; + tAdd->LastAccess = pReplUser1->LastAccess; + tAdd->Flags = pReplUser1->Flags & ( LLS_FLAG_SUITE_USE | LLS_FLAG_SUITE_AUTO ); + + RtlAcquireResourceShared(&MasterServiceListLock, TRUE); + tAdd->Service = MasterServiceTable[pReplUser1->Service]; + RtlReleaseResource(&MasterServiceListLock); + } + + // + // Now add it to our cache + // + tAdd->prev = pAdd; + pAdd = tAdd; + + // + // Keep track of first on (bottom on stack) so we can append + // it onto the real add cache. + // + if (lAdd == NULL) + lAdd = pAdd; + + CacheSize++; + } + } else + ASSERT(FALSE); + } + + // + // Now that we've walked through all the users - update the actual + // Add Cache. + // + if (pAdd != NULL) { + RtlEnterCriticalSection(&AddCacheLock); + lAdd->prev = AddCache; + AddCache = pAdd; + AddCacheSize += CacheSize; + RtlLeaveCriticalSection(&AddCacheLock); + + // + // Now must signal the event so we can pull off the new record. + // + Status = NtSetEvent( LLSAddCacheEvent, NULL ); + ASSERT(NT_SUCCESS(Status)); + + } + +} // UserListUnpack + + +///////////////////////////////////////////////////////////////////////// +NTSTATUS +UserListStringsPack ( + ULONG UserLevel, + + ULONG UserTableSize, + LPVOID Users, + + ULONG *pUserStringSize, + LPTSTR *pUserStrings + ) + +/*++ + +Routine Description: + + +Arguments: + +Return Value: + + +--*/ + +{ + NTSTATUS Status = STATUS_SUCCESS; + ULONG i; + ULONG StringSize; + LPTSTR UserStrings = NULL; + TCHAR *pStr; + +#if DBG + if (TraceFlags & TRACE_FUNCTION_TRACE) + dprintf(TEXT("UserListStringsPack\n")); +#endif + + ASSERT(pUserStrings != NULL); + ASSERT(pUserStringSize != NULL); + + *pUserStrings = NULL; + *pUserStringSize = 0; + + // + // First walk the list adding up string sizes - to calculate our buff size + // + StringSize = 0; + for (i = 0; i < UserTableSize; i++) { + if ( 0 == UserLevel ) + { + StringSize += 1 + lstrlen( ((PREPL_USER_RECORD_0) Users)[i].Name ); + } + else + { + StringSize += 1 + lstrlen( ((PREPL_USER_RECORD_1) Users)[i].Name ); + } + } + + // + // Make sure there is anything to replicate + // + if (StringSize > 0) { + // + // Create our buffer to hold all of the garbage + // + UserStrings = MIDL_user_allocate(StringSize * sizeof(TCHAR)); + if (UserStrings == NULL) { + ASSERT(FALSE); + return STATUS_NO_MEMORY; + } + + // + // Fill in the buffer + // + pStr = UserStrings; + for (i = 0; i < UserTableSize; i++) { + if ( 0 == UserLevel ) + { + lstrcpy( pStr, ((PREPL_USER_RECORD_0) Users)[i].Name ); + } + else + { + lstrcpy( pStr, ((PREPL_USER_RECORD_1) Users)[i].Name ); + } + + pStr += 1 + lstrlen( pStr ); + } + } + + *pUserStrings = UserStrings; + *pUserStringSize = StringSize; + + return Status; +} // UserListStringsPack + + +///////////////////////////////////////////////////////////////////////// +VOID +UserListStringsUnpack ( + ULONG UserLevel, + + ULONG UserTableSize, + LPVOID Users, + + ULONG UserStringSize, + LPTSTR UserStrings + ) + +/*++ + +Routine Description: + + +Arguments: + +Return Value: + + +--*/ + +{ + ULONG i; + TCHAR *pStr; + +#if DBG + if (TraceFlags & TRACE_FUNCTION_TRACE) + dprintf(TEXT("UserListStringsUnpack\n")); +#endif + + pStr = UserStrings; + for (i = 0; i < UserTableSize; i++) { + if ( 0 == UserLevel ) + { + ((PREPL_USER_RECORD_0) Users)[i].Name = pStr; + } + else + { + ((PREPL_USER_RECORD_1) Users)[i].Name = pStr; + } + + // + // Move to end of current string + // + while (*pStr != TEXT('\0')) + pStr++; + + // now go past ending NULL + pStr++; + } + +} // UserListStringsUnpack + + + +///////////////////////////////////////////////////////////////////////// +// Service List +// + +///////////////////////////////////////////////////////////////////////// +NTSTATUS +ServiceListPack ( + ULONG *pServiceTableSize, + PREPL_SERVICE_RECORD *pServices + ) + +/*++ + +Routine Description: + + +Arguments: + +Return Value: + + +--*/ + +{ + NTSTATUS Status = STATUS_SUCCESS; + PREPL_SERVICE_RECORD Services = NULL; + ULONG i; + ULONG TotalRecords = 0; + PMASTER_SERVICE_RECORD pService; + +#if DBG + if (TraceFlags & TRACE_FUNCTION_TRACE) + dprintf(TEXT("LLS TRACE: ServiceListPack\n")); +#endif + + ASSERT(pServices != NULL); + ASSERT(pServiceTableSize != NULL); + *pServices = NULL; + *pServiceTableSize = 0; + + TotalRecords = MasterServiceListSize; + + // + // Make sure there is anything to replicate + // + if (TotalRecords > 0) { + // + // Create our buffer to hold all of the garbage + // + Services = MIDL_user_allocate(TotalRecords * sizeof(REPL_SERVICE_RECORD)); + if (Services == NULL) { + ASSERT(FALSE); + return STATUS_NO_MEMORY; + } + + // + // Fill in the buffer - walk the user tree + // + for (i = 0; i < MasterServiceListSize; i++) { + pService = MasterServiceTable[i]; + + Services[i].Name = pService->Name; + Services[i].FamilyName = pService->Family->Name; + Services[i].Version = pService->Version; + Services[i].Index = pService->Index; + } + } + +#if DBG + if (TraceFlags & TRACE_REPLICATION) + dprintf(TEXT("ServiceListPack: [%lu]\n"), TotalRecords); +#endif + *pServices = Services; + *pServiceTableSize = TotalRecords; + return Status; +} // ServiceListPack + + +///////////////////////////////////////////////////////////////////////// +VOID +ServiceListUnpack ( + ULONG ServiceTableSize, + PREPL_SERVICE_RECORD Services, + + ULONG ServerTableSize, + PREPL_SERVER_RECORD Servers, + + ULONG ServerServiceTableSize, + PREPL_SERVER_SERVICE_RECORD ServerServices + ) + +/*++ + +Routine Description: + + +Arguments: + +Return Value: + + +--*/ + +{ + ULONG i, j; + PMASTER_SERVICE_RECORD pService; + PREPL_SERVICE_RECORD pSvc; + +#if DBG + if (TraceFlags & (TRACE_REPLICATION | TRACE_FUNCTION_TRACE)) + dprintf(TEXT("ServiceListUnpack: [%lu]\n"), ServiceTableSize); +#endif + // + // Walk services table, adding any new services to our local table. + // Fix up the index pointers to match our local services. + // + RtlAcquireResourceExclusive(&MasterServiceListLock, TRUE); + + for (i = 0; i < ServiceTableSize; i++) { + pSvc = &Services[i]; + pService = MasterServiceListAdd(pSvc->FamilyName, pSvc->Name, pSvc->Version ); + + if (pService != NULL) { + pSvc->Index = pService->Index; + + // + // In case this got added from the local service list table and we + // didn't have a version # yet. + // + if ( (pService->Version == 0) && (pSvc->Version != 0) ) { + PMASTER_SERVICE_ROOT ServiceRoot = NULL; + + // + // Fixup next pointer chain + // + ServiceRoot = pService->Family; + j = 0; + while ((j < ServiceRoot->ServiceTableSize) && (MasterServiceTable[ServiceRoot->Services[j]]->Version < pSvc->Version)) + j++; + + pService->next = 0; + pService->Version = pSvc->Version; + if (j > 0) { + if (MasterServiceTable[ServiceRoot->Services[j - 1]]->next == pService->Index + 1) + pService->next = 0; + else + pService->next = MasterServiceTable[ServiceRoot->Services[j - 1]]->next; + + if (MasterServiceTable[ServiceRoot->Services[j - 1]] != pService) + MasterServiceTable[ServiceRoot->Services[j - 1]]->next = pService->Index + 1; + + } + + // Resort it in order of the versions + qsort((void *) ServiceRoot->Services, (size_t) ServiceRoot->ServiceTableSize, sizeof(PMASTER_SERVICE_RECORD), MServiceRecordCompare); + } + + } else { + ASSERT(FALSE); + pSvc->Index = 0; + } + + } + + RtlReleaseResource(&MasterServiceListLock); + +} // ServiceListUnpack + + +///////////////////////////////////////////////////////////////////////// +NTSTATUS +ServiceListStringsPack ( + ULONG ServiceTableSize, + PREPL_SERVICE_RECORD Services, + + ULONG *pServiceStringSize, + LPTSTR *pServiceStrings + ) + +/*++ + +Routine Description: + + +Arguments: + +Return Value: + + +--*/ + +{ + NTSTATUS Status = STATUS_SUCCESS; + ULONG i; + ULONG StringSize; + PREPL_SERVICE_RECORD pService; + LPTSTR ServiceStrings = NULL; + TCHAR *pStr; + +#if DBG + if (TraceFlags & TRACE_FUNCTION_TRACE) + dprintf(TEXT("ServiceListStringsPack\n")); +#endif + + ASSERT(pServiceStrings != NULL); + ASSERT(pServiceStringSize != NULL); + + *pServiceStrings = NULL; + *pServiceStringSize = 0; + + // + // First walk the list adding up string sizes - to calculate our buff size + // + StringSize = 0; + for (i = 0; i < ServiceTableSize; i++) { + pService = &Services[i]; + + StringSize = StringSize + lstrlen(pService->Name) + 1; + StringSize = StringSize + lstrlen(pService->FamilyName) + 1; + } + + // + // Make sure there is anything to replicate + // + if (StringSize > 0) { + // + // Create our buffer to hold all of the garbage + // + ServiceStrings = MIDL_user_allocate(StringSize * sizeof(TCHAR)); + if (ServiceStrings == NULL) { + ASSERT(FALSE); + return STATUS_NO_MEMORY; + } + + // + // Fill in the buffer + // + pStr = ServiceStrings; + for (i = 0; i < ServiceTableSize; i++) { + pService = &Services[i]; + + lstrcpy(pStr, pService->Name); + pStr = &pStr[lstrlen(pService->Name) + 1]; + + lstrcpy(pStr, pService->FamilyName); + pStr = &pStr[lstrlen(pService->FamilyName) + 1]; + } + } + + *pServiceStrings = ServiceStrings; + *pServiceStringSize = StringSize; + + return Status; +} // ServiceListStringsPack + + +///////////////////////////////////////////////////////////////////////// +VOID +ServiceListStringsUnpack ( + ULONG ServiceTableSize, + PREPL_SERVICE_RECORD Services, + + ULONG ServiceStringSize, + LPTSTR ServiceStrings + ) + +/*++ + +Routine Description: + + +Arguments: + +Return Value: + + +--*/ + +{ + ULONG i; + PREPL_SERVICE_RECORD pService; + TCHAR *pStr; + +#if DBG + if (TraceFlags & TRACE_FUNCTION_TRACE) + dprintf(TEXT("ServiceListStringsUnpack\n")); +#endif + + pStr = ServiceStrings; + for (i = 0; i < ServiceTableSize; i++) { + pService = &Services[i]; + + pService->Name = pStr; + + // + // Move to end of current string + // + while (*pStr != TEXT('\0')) + pStr++; + + // now go past ending NULL + pStr++; + + pService->FamilyName = pStr; + + // + // Move to end of current string + // + while (*pStr != TEXT('\0')) + pStr++; + + // now go past ending NULL + pStr++; + } + +} // ServiceListStringsUnpack + + + +///////////////////////////////////////////////////////////////////////// +// Server List +// + +///////////////////////////////////////////////////////////////////////// +NTSTATUS +ServerListPack ( + ULONG *pServerTableSize, + PREPL_SERVER_RECORD *pServers + ) + +/*++ + +Routine Description: + + +Arguments: + +Return Value: + + +--*/ + +{ + NTSTATUS Status = STATUS_SUCCESS; + PREPL_SERVER_RECORD Servers = NULL; + ULONG i; + ULONG TotalRecords = 0; + PSERVER_RECORD pServer; + +#if DBG + if (TraceFlags & TRACE_FUNCTION_TRACE) + dprintf(TEXT("LLS TRACE: ServerListPack\n")); +#endif + + ASSERT(pServers != NULL); + ASSERT(pServerTableSize != NULL); + + *pServers = NULL; + *pServerTableSize = 0; + + TotalRecords = ServerListSize; + + // + // Make sure there is anything to replicate + // + if (TotalRecords > 0) { + // + // Create our buffer to hold all of the garbage + // + Servers = MIDL_user_allocate(TotalRecords * sizeof(REPL_SERVER_RECORD)); + if (Servers == NULL) { + ASSERT(FALSE); + return STATUS_NO_MEMORY; + } + + // + // Fill in the buffer - walk the user tree + // + for (i = 0; i < ServerListSize; i++) { + pServer = ServerTable[i]; + + Servers[i].Name = pServer->Name; + Servers[i].MasterServer = pServer->MasterServer; + Servers[i].Index = pServer->Index; + } + } + +#if DBG + if (TraceFlags & TRACE_REPLICATION) + dprintf(TEXT("ServerListPack: [%lu]\n"), TotalRecords); +#endif + *pServers = Servers;; + *pServerTableSize = TotalRecords; + return Status; +} // ServerListPack + + +///////////////////////////////////////////////////////////////////////// +VOID +ServerListUnpack ( + ULONG ServiceTableSize, + PREPL_SERVICE_RECORD Services, + + ULONG ServerTableSize, + PREPL_SERVER_RECORD Servers, + + ULONG ServerServiceTableSize, + PREPL_SERVER_SERVICE_RECORD ServerServices + ) + +/*++ + +Routine Description: + + +Arguments: + +Return Value: + + +--*/ + +{ + ULONG i; + PSERVER_RECORD pServer; + PREPL_SERVER_RECORD pSrv; + TCHAR ComputerName[MAX_COMPUTERNAME_LENGTH + 1]; + +#if DBG + if (TraceFlags & (TRACE_REPLICATION | TRACE_FUNCTION_TRACE)) + dprintf(TEXT("ServerListUnpack: [%lu]\n"), ServerTableSize); +#endif + + // + // Walk server table, adding any new servers to our local table. + // Fix up the index pointers to match our local table and re-fix + // Service table pointers. + // + RtlEnterCriticalSection(&ConfigInfoLock); + lstrcpy(ComputerName, ConfigInfo.ComputerName); + RtlLeaveCriticalSection(&ConfigInfoLock); + + RtlAcquireResourceExclusive(&ServerListLock, TRUE); + + for (i = 0; i < ServerTableSize; i++) { + pSrv = &Servers[i]; + + if (pSrv->MasterServer != 0) + pServer = ServerListAdd(pSrv->Name, Servers[pSrv->MasterServer - 1].Name); + else + pServer = ServerListAdd(pSrv->Name, ComputerName); + + if (pServer != NULL) + pSrv->Index = pServer->Index; + else { + ASSERT(FALSE); + pSrv->Index = 0; + } + } + + RtlReleaseResource(&ServerListLock); + +} // ServerListUnpack + + +///////////////////////////////////////////////////////////////////////// +NTSTATUS +ServerServiceListPack ( + ULONG *pServerServiceTableSize, + PREPL_SERVER_SERVICE_RECORD *pServerServices + ) + +/*++ + +Routine Description: + + +Arguments: + +Return Value: + + +--*/ + +{ + NTSTATUS Status = STATUS_SUCCESS; + PREPL_SERVER_SERVICE_RECORD ServerServices = NULL; + ULONG i, j, k; + ULONG TotalRecords = 0; + PSERVER_RECORD pServer; + PSERVER_SERVICE_RECORD pServerService; + +#if DBG + if (TraceFlags & TRACE_FUNCTION_TRACE) + dprintf(TEXT("LLS TRACE: ServerServiceListPack\n")); +#endif + + ASSERT(pServerServices != NULL); + ASSERT(pServerServiceTableSize != NULL); + + *pServerServices = NULL; + *pServerServiceTableSize = 0; + + // + // Walk the ServerList and find all ServiceRecords + for (i = 0; i < ServerListSize; i++) + TotalRecords += ServerTable[i]->ServiceTableSize; + + // + // Make sure there is anything to replicate + // + if (TotalRecords > 0) { + // + // Create our buffer to hold all of the garbage + // + ServerServices = MIDL_user_allocate(TotalRecords * sizeof(REPL_SERVER_SERVICE_RECORD)); + if (ServerServices == NULL) { + ASSERT(FALSE); + return STATUS_NO_MEMORY; + } + + // + // Fill in the buffer - walk the user tree + // + k = 0; + for (i = 0; i < ServerListSize; i++) { + pServer = ServerTable[i]; + + for (j = 0; j < pServer->ServiceTableSize; j++) { + ServerServices[k].Server = pServer->Index; + ServerServices[k].Service = pServer->Services[j]->Service; + ServerServices[k].MaxSessionCount = pServer->Services[j]->MaxSessionCount; + ServerServices[k].MaxSetSessionCount = pServer->Services[j]->MaxSetSessionCount; + ServerServices[k].HighMark = pServer->Services[j]->HighMark; + k++; + } + } + } + +#if DBG + if (TraceFlags & TRACE_REPLICATION) + dprintf(TEXT("ServerServiceListPack: [%lu]\n"), TotalRecords); +#endif + *pServerServices = ServerServices; + *pServerServiceTableSize = TotalRecords; + return Status; +} // ServerServiceListPack + + +///////////////////////////////////////////////////////////////////////// +VOID +ServerServiceListUnpack ( + ULONG ServiceTableSize, + PREPL_SERVICE_RECORD Services, + + ULONG ServerTableSize, + PREPL_SERVER_RECORD Servers, + + ULONG ServerServiceTableSize, + PREPL_SERVER_SERVICE_RECORD ServerServices + ) + +/*++ + +Routine Description: + + +Arguments: + +Return Value: + + +--*/ + +{ + ULONG i; + PSERVER_RECORD pServer; + PREPL_SERVER_SERVICE_RECORD pSrv; + PSERVER_SERVICE_RECORD pService; + PMASTER_SERVICE_RECORD pMasterService; + +#if DBG + if (TraceFlags & (TRACE_REPLICATION | TRACE_FUNCTION_TRACE)) + dprintf(TEXT("ServerServiceListUnpack: [%lu]\n"), ServerServiceTableSize); +#endif + // + // Walk server table, adding any new servers to our local table. + // Fix up the index pointers to match our local table and re-fix + // Service table pointers. + // + + RtlAcquireResourceExclusive(&ServerListLock, TRUE); + RtlAcquireResourceShared(&MasterServiceListLock, TRUE); + + for (i = 0; i < ServerServiceTableSize; i++) { + pSrv = &ServerServices[i]; + pServer = ServerListFind(Servers[pSrv->Server - 1].Name); + ASSERT(pServer != NULL); + + if (pServer != NULL) { + BOOL bReplaceValues; + + pService = ServerServiceListFind(Services[pSrv->Service].Name, pServer->ServiceTableSize, pServer->Services); + bReplaceValues = ( NULL != pService ); + + pService = ServerServiceListAdd(Services[pSrv->Service].Name, + Services[pSrv->Service].Index, + &pServer->ServiceTableSize, + &pServer->Services); + + ASSERT(pService != NULL); + + // + // Remove any old info + // + pMasterService = MasterServiceTable[Services[pSrv->Service].Index]; + if ( bReplaceValues ) + { + pMasterService->MaxSessionCount -= pService->MaxSessionCount; + pMasterService->HighMark -= pService->HighMark; + } + + // + // Now update new info + // + pService->MaxSessionCount = pSrv->MaxSessionCount; + pService->HighMark = pSrv->HighMark; + pMasterService->MaxSessionCount += pService->MaxSessionCount; + pMasterService->HighMark += pService->HighMark; + } + + } + + RtlReleaseResource(&MasterServiceListLock); + RtlReleaseResource(&ServerListLock); + +} // ServerServiceListUnpack + + +///////////////////////////////////////////////////////////////////////// +NTSTATUS +ServerListStringsPack ( + ULONG ServerTableSize, + PREPL_SERVER_RECORD Servers, + + ULONG *pServerStringSize, + LPTSTR *pServerStrings + ) + +/*++ + +Routine Description: + + +Arguments: + +Return Value: + + +--*/ + +{ + NTSTATUS Status = STATUS_SUCCESS; + ULONG i; + ULONG StringSize; + PREPL_SERVER_RECORD pServer; + LPTSTR ServerStrings = NULL; + TCHAR *pStr; + +#if DBG + if (TraceFlags & TRACE_FUNCTION_TRACE) + dprintf(TEXT("ServerListStringsPack\n")); +#endif + + ASSERT(pServerStrings != NULL); + ASSERT(pServerStringSize != NULL); + + *pServerStrings = NULL; + *pServerStringSize = 0; + + // + // First walk the list adding up string sizes - to calculate our buff size + // + StringSize = 0; + for (i = 0; i < ServerTableSize; i++) { + pServer = &Servers[i]; + + StringSize = StringSize + lstrlen(pServer->Name) + 1; + } + + // + // Make sure there is anything to replicate + // + if (StringSize > 0) { + // + // Create our buffer to hold all of the garbage + // + ServerStrings = MIDL_user_allocate(StringSize * sizeof(TCHAR)); + if (ServerStrings == NULL) { + ASSERT(FALSE); + return STATUS_NO_MEMORY; + } + + // + // Fill in the buffer + // + pStr = ServerStrings; + for (i = 0; i < ServerTableSize; i++) { + pServer = &Servers[i]; + + lstrcpy(pStr, pServer->Name); + pStr = &pStr[lstrlen(pServer->Name) + 1]; + } + } + + *pServerStrings = ServerStrings; + *pServerStringSize = StringSize; + + return Status; +} // ServerListStringsPack + + +///////////////////////////////////////////////////////////////////////// +VOID +ServerListStringsUnpack ( + ULONG ServerTableSize, + PREPL_SERVER_RECORD Servers, + + ULONG ServerStringSize, + LPTSTR ServerStrings + ) + +/*++ + +Routine Description: + + +Arguments: + +Return Value: + + +--*/ + +{ + ULONG i; + PREPL_SERVER_RECORD pServer; + TCHAR *pStr; + +#if DBG + if (TraceFlags & TRACE_FUNCTION_TRACE) + dprintf(TEXT("ServerListStringsUnpack\n")); +#endif + + // + // First do license service strings + // + pStr = ServerStrings; + for (i = 0; i < ServerTableSize; i++) { + pServer = &Servers[i]; + + pServer->Name = pStr; + + // + // Move to end of current string + // + while (*pStr != TEXT('\0')) + pStr++; + + // now go past ending NULL + pStr++; + } + +} // ServerListStringsUnpack + + + +///////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////// + +///////////////////////////////////////////////////////////////////////// +NTSTATUS +PackAll ( + DWORD LastReplicated, + + ULONG *pServiceTableSize, + PREPL_SERVICE_RECORD *pServices, + + ULONG *pServerTableSize, + PREPL_SERVER_RECORD *pServers, + + ULONG *pServerServiceTableSize, + PREPL_SERVER_SERVICE_RECORD *pServerServices, + + ULONG UserLevel, + ULONG *pUserTableSize, + LPVOID *pUsers + ) + +/*++ + +Routine Description: + + +Arguments: + +Return Value: + + +--*/ + +{ + NTSTATUS Status = STATUS_SUCCESS; + +#if DBG + if (TraceFlags & TRACE_FUNCTION_TRACE) + dprintf(TEXT("LLS TRACE: PackAll\n")); +#endif + + // + // We need to grab all the locks here so that a service isn't snuck in + // behind our backs - since these tables interact with each other. + // + RtlAcquireResourceExclusive(&UserListAddEnumLock, TRUE); + RtlAcquireResourceShared(&UserListLock, TRUE); + RtlAcquireResourceShared(&MasterServiceListLock, TRUE); + RtlAcquireResourceShared(&ServerListLock, TRUE); + + Status = ServiceListPack(pServiceTableSize, pServices); + if (Status != STATUS_SUCCESS) + goto PackAllExit; + + Status = ServerListPack(pServerTableSize, pServers); + if (Status != STATUS_SUCCESS) + goto PackAllExit; + + Status = ServerServiceListPack(pServerServiceTableSize, pServerServices); + if (Status != STATUS_SUCCESS) + goto PackAllExit; + + Status = UserListPack(LastReplicated, UserLevel, pUserTableSize, pUsers); + if (Status != STATUS_SUCCESS) + goto PackAllExit; + + // + // Now update our last used time + // + LastUsedTime = DateSystemGet() + 1; + +PackAllExit: + RtlReleaseResource(&ServerListLock); + RtlReleaseResource(&MasterServiceListLock); + RtlReleaseResource(&UserListAddEnumLock); + RtlReleaseResource(&UserListLock); + + return Status; +} // PackAll + + +///////////////////////////////////////////////////////////////////////// +VOID +UnpackAll ( + ULONG ServiceTableSize, + PREPL_SERVICE_RECORD Services, + + ULONG ServerTableSize, + PREPL_SERVER_RECORD Servers, + + ULONG ServerServiceTableSize, + PREPL_SERVER_SERVICE_RECORD ServerServices, + + ULONG UserLevel, + ULONG UserTableSize, + LPVOID Users + ) + +/*++ + +Routine Description: + + +Arguments: + +Return Value: + + +--*/ + +{ +#if DBG + if (TraceFlags & TRACE_FUNCTION_TRACE) + dprintf(TEXT("LLS TRACE: UnpackAll\n")); +#endif + + ServiceListUnpack(ServiceTableSize, Services, ServerTableSize, Servers, ServerServiceTableSize, ServerServices); + ServerListUnpack(ServiceTableSize, Services, ServerTableSize, Servers, ServerServiceTableSize, ServerServices); + ServerServiceListUnpack(ServiceTableSize, Services, ServerTableSize, Servers, ServerServiceTableSize, ServerServices); + UserListUnpack(ServiceTableSize, Services, ServerTableSize, Servers, ServerServiceTableSize, ServerServices, UserLevel, UserTableSize, Users); +} // UnpackAll + + +///////////////////////////////////////////////////////////////////////// +VOID +LLSDataLoad() + +/*++ + +Routine Description: + +Arguments: + + None. + +Return Value: + + None. + +--*/ + +{ + BOOL ret; + DWORD Version, DataSize; + NTSTATUS Status = STATUS_SUCCESS; + HANDLE hFile = NULL; + + ULONG ServiceTableSize = 0; + PREPL_SERVICE_RECORD Services = NULL; + + ULONG ServiceStringSize; + LPTSTR ServiceStrings = NULL; + + ULONG ServerServiceTableSize = 0; + PREPL_SERVER_SERVICE_RECORD ServerServices = NULL; + + ULONG ServerTableSize = 0; + PREPL_SERVER_RECORD Servers = NULL; + + ULONG ServerStringSize; + LPTSTR ServerStrings = NULL; + + ULONG UserTableSize = 0; + LPVOID Users = NULL; + + ULONG UserStringSize; + LPTSTR UserStrings = NULL; + + LLS_DATA_FILE_HEADER FileHeader; + DWORD BytesRead; + +#if DBG + if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_DATABASE)) + dprintf(TEXT("LLS TRACE: LLSDataLoad\n")); +#endif + + // + // If nothing to load then get-out + // + if (!FileExists(UserFileName)) + goto LLSDataLoadExit; + + // + // Check the init header + // + Version = DataSize = 0; + hFile = LlsFileCheck(UserFileName, &Version, &DataSize ); + if (hFile == NULL) { + Status = GetLastError(); + goto LLSDataLoadExit; + } + + if ( ( ( Version != USER_FILE_VERSION_0 ) + || ( DataSize != sizeof(LLS_DATA_FILE_HEADER_0) ) ) + && ( ( Version != USER_FILE_VERSION ) + || ( DataSize != sizeof(LLS_DATA_FILE_HEADER) ) ) ) + { + Status = STATUS_FILE_INVALID; + goto LLSDataLoadExit; + } + + // + // The init header checks out, so load the license header and data blocks + // + if ( USER_FILE_VERSION_0 == Version ) + { + // 3.51 data file + LLS_DATA_FILE_HEADER_0 FileHeader0; + + ret = ReadFile(hFile, &FileHeader0, sizeof(LLS_DATA_FILE_HEADER_0), &BytesRead, NULL); + + if ( ret ) + { + FileHeader.ServiceLevel = 0; + FileHeader.ServiceTableSize = FileHeader0.ServiceTableSize; + FileHeader.ServiceStringSize = FileHeader0.ServiceStringSize; + FileHeader.ServerLevel = 0; + FileHeader.ServerTableSize = FileHeader0.ServerTableSize; + FileHeader.ServerStringSize = FileHeader0.ServerStringSize; + FileHeader.ServerServiceLevel = 0; + FileHeader.ServerServiceTableSize = FileHeader0.ServerServiceTableSize; + FileHeader.UserLevel = 0; + FileHeader.UserTableSize = FileHeader0.UserTableSize; + FileHeader.UserStringSize = FileHeader0.UserStringSize; + } + } + else + { + ret = ReadFile(hFile, &FileHeader, sizeof(LLS_DATA_FILE_HEADER), &BytesRead, NULL); + } + + if ( ret ) + { + // header read okay; ensure data type levels are okay + if ( ( 0 != FileHeader.ServiceLevel ) + || ( 0 != FileHeader.ServerLevel ) + || ( 0 != FileHeader.ServerServiceLevel ) + || ( ( 0 != FileHeader.UserLevel ) + && ( 1 != FileHeader.UserLevel ) ) ) + { + Status = STATUS_FILE_INVALID; + goto LLSDataLoadExit; + } + } + + ServiceTableSize = 0; + ServiceStringSize = 0; + ServerServiceTableSize = 0; + ServerTableSize = 0; + ServerStringSize = 0; + UserTableSize = 0; + UserStringSize = 0; + + if (ret) { + // + // Run through and allocate space to read data blocks into + // + if (FileHeader.ServiceTableSize != 0) { + ServiceTableSize = FileHeader.ServiceTableSize / sizeof(REPL_SERVICE_RECORD); + Services = MIDL_user_allocate(FileHeader.ServiceTableSize); + + if ( Services == NULL ) { + Status = STATUS_NO_MEMORY; + goto LLSDataLoadExit; + } + } + + if (FileHeader.ServiceStringSize != 0) { + ServiceStringSize = FileHeader.ServiceStringSize / sizeof(TCHAR); + ServiceStrings = MIDL_user_allocate(FileHeader.ServiceStringSize); + + if ( ServiceStrings == NULL ) { + Status = STATUS_NO_MEMORY; + goto LLSDataLoadExit; + } + } + + if (FileHeader.ServerTableSize != 0) { + ServerTableSize = FileHeader.ServerTableSize / sizeof(REPL_SERVER_RECORD); + Servers = MIDL_user_allocate(FileHeader.ServerTableSize); + + if ( Servers == NULL ) { + Status = STATUS_NO_MEMORY; + goto LLSDataLoadExit; + } + } + + if (FileHeader.ServerStringSize != 0) { + ServerStringSize = FileHeader.ServerStringSize / sizeof(TCHAR); + ServerStrings = MIDL_user_allocate(FileHeader.ServerStringSize); + + if ( ServerStrings == NULL ) { + Status = STATUS_NO_MEMORY; + goto LLSDataLoadExit; + } + } + + if (FileHeader.ServerServiceTableSize != 0) { + ServerServiceTableSize = FileHeader.ServerServiceTableSize / sizeof(REPL_SERVER_SERVICE_RECORD); + ServerServices = MIDL_user_allocate(FileHeader.ServerServiceTableSize); + + if ( ServerServices == NULL ) { + Status = STATUS_NO_MEMORY; + goto LLSDataLoadExit; + } + } + + if (FileHeader.UserTableSize != 0) { + UserTableSize = FileHeader.UserTableSize / ( FileHeader.UserLevel ? sizeof(REPL_USER_RECORD_1) + : sizeof(REPL_USER_RECORD_0) ); + Users = MIDL_user_allocate(FileHeader.UserTableSize); + + if ( Users == NULL ) { + Status = STATUS_NO_MEMORY; + goto LLSDataLoadExit; + } + } + + if (FileHeader.UserStringSize != 0) { + UserStringSize = FileHeader.UserStringSize / sizeof(TCHAR); + UserStrings = MIDL_user_allocate(FileHeader.UserStringSize); + + if ( UserStrings == NULL ) { + Status = STATUS_NO_MEMORY; + goto LLSDataLoadExit; + } + } + + } + + if (ret && (FileHeader.ServiceTableSize != 0) ) + ret = ReadFile(hFile, Services, FileHeader.ServiceTableSize, &BytesRead, NULL); + + if (ret && (FileHeader.ServiceStringSize != 0) ) + ret = ReadFile(hFile, ServiceStrings, FileHeader.ServiceStringSize, &BytesRead, NULL); + + if (ret && (FileHeader.ServerTableSize != 0) ) + ret = ReadFile(hFile, Servers, FileHeader.ServerTableSize, &BytesRead, NULL); + + if (ret && (FileHeader.ServerStringSize != 0) ) + ret = ReadFile(hFile, ServerStrings, FileHeader.ServerStringSize, &BytesRead, NULL); + + if (ret && (FileHeader.ServerServiceTableSize != 0) ) + ret = ReadFile(hFile, ServerServices, FileHeader.ServerServiceTableSize, &BytesRead, NULL); + + if (ret && (FileHeader.UserTableSize != 0) ) + ret = ReadFile(hFile, Users, FileHeader.UserTableSize, &BytesRead, NULL); + + if (ret && (FileHeader.UserStringSize != 0) ) + ret = ReadFile(hFile, UserStrings, FileHeader.UserStringSize, &BytesRead, NULL); + + if (!ret) { + Status = GetLastError(); + goto LLSDataLoadExit; + } + + // + // Decrypt the data + // + Status = DeBlock(Services, FileHeader.ServiceTableSize); + + if (Status == STATUS_SUCCESS) + Status = DeBlock(ServiceStrings, FileHeader.ServiceStringSize); + + if (Status == STATUS_SUCCESS) + Status = DeBlock(Servers, FileHeader.ServerTableSize); + + if (Status == STATUS_SUCCESS) + Status = DeBlock(ServerStrings, FileHeader.ServerStringSize); + + if (Status == STATUS_SUCCESS) + Status = DeBlock(ServerServices, FileHeader.ServerServiceTableSize); + + if (Status == STATUS_SUCCESS) + Status = DeBlock(Users, FileHeader.UserTableSize); + + if (Status == STATUS_SUCCESS) + Status = DeBlock(UserStrings, FileHeader.UserStringSize); + + if (Status != STATUS_SUCCESS) + goto LLSDataLoadExit; + + + // + // Unpack the string data + // + ServiceListStringsUnpack( ServiceTableSize, Services, ServiceStringSize, ServiceStrings ); + ServerListStringsUnpack( ServerTableSize, Servers, ServerStringSize, ServerStrings ); + UserListStringsUnpack( FileHeader.UserLevel, UserTableSize, Users, UserStringSize, UserStrings ); + + // + // Unpack the data + // + UnpackAll ( ServiceTableSize, Services, ServerTableSize, Servers, + ServerServiceTableSize, ServerServices, + FileHeader.UserLevel, UserTableSize, Users ); + +LLSDataLoadExit: + + if (hFile != NULL) + CloseHandle(hFile); + + // + // Run through our tables and clean them up + // + if (Services != NULL) + MIDL_user_free(Services); + + if (ServiceStrings != NULL) + MIDL_user_free(ServiceStrings); + + if (Servers != NULL) + MIDL_user_free(Servers); + + if (ServerStrings != NULL) + MIDL_user_free(ServerStrings); + + if (ServerServices != NULL) + MIDL_user_free(ServerServices); + + if (Users != NULL) + MIDL_user_free(Users); + + if (UserStrings != NULL) + MIDL_user_free(UserStrings); + + // + // If there was an error log it. + // + if (Status != STATUS_SUCCESS) + LogEvent(LLS_EVENT_LOAD_USER, 0, NULL, Status); + +} // LLSDataLoad + + +///////////////////////////////////////////////////////////////////////// +NTSTATUS +LLSDataSave() + +/*++ + +Routine Description: + +Arguments: + + None. + +Return Value: + + None. + +--*/ + +{ + BOOL ret = TRUE; + NTSTATUS Status = STATUS_SUCCESS; + HANDLE hFile = NULL; + + ULONG ServiceTableSize = 0; + PREPL_SERVICE_RECORD Services = NULL; + + ULONG ServiceStringSize; + LPTSTR ServiceStrings = NULL; + + ULONG ServerServiceTableSize = 0; + PREPL_SERVER_SERVICE_RECORD ServerServices = NULL; + + ULONG ServerTableSize = 0; + PREPL_SERVER_RECORD Servers = NULL; + + ULONG ServerStringSize; + LPTSTR ServerStrings = NULL; + + ULONG UserTableSize = 0; + PREPL_USER_RECORD_1 Users = NULL; + + ULONG UserStringSize; + LPTSTR UserStrings = NULL; + + LLS_DATA_FILE_HEADER FileHeader; + DWORD BytesWritten; + +#if DBG + if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_DATABASE)) + dprintf(TEXT("LLS TRACE: LLSDataSave\n")); +#endif + + // + // Pack the data + // + Status = PackAll ( 0, + &ServiceTableSize, &Services, + &ServerTableSize, &Servers, + &ServerServiceTableSize, &ServerServices, + 1, &UserTableSize, &Users ); + if (Status != STATUS_SUCCESS) + goto LLSDataSaveExit; + + // + // Now pack the String data + // + Status = ServiceListStringsPack( ServiceTableSize, Services, &ServiceStringSize, &ServiceStrings ); + if (Status != STATUS_SUCCESS) + goto LLSDataSaveExit; + + Status = ServerListStringsPack( ServerTableSize, Servers, &ServerStringSize, &ServerStrings ); + if (Status != STATUS_SUCCESS) + goto LLSDataSaveExit; + + Status = UserListStringsPack( 1, UserTableSize, Users, &UserStringSize, &UserStrings ); + if (Status != STATUS_SUCCESS) + goto LLSDataSaveExit; + + // + // Fill out the file header - sizes are byte sizes + // + FileHeader.ServiceTableSize = ServiceTableSize * sizeof(REPL_SERVICE_RECORD); + FileHeader.ServiceStringSize = ServiceStringSize * sizeof(TCHAR); + FileHeader.ServerTableSize = ServerTableSize * sizeof(REPL_SERVER_RECORD); + FileHeader.ServerStringSize = ServerStringSize * sizeof(TCHAR); + FileHeader.ServerServiceTableSize = ServerServiceTableSize * sizeof(REPL_SERVER_SERVICE_RECORD); + FileHeader.UserTableSize = UserTableSize * sizeof(REPL_USER_RECORD_1); + FileHeader.UserStringSize = UserStringSize * sizeof(TCHAR); + + FileHeader.ServiceLevel = 0; + FileHeader.ServerLevel = 0; + FileHeader.ServerServiceLevel = 0; + FileHeader.UserLevel = 1; + + // + // Encrypt the data before saving it out. + // + Status = EBlock(Services, FileHeader.ServiceTableSize); + + if (Status == STATUS_SUCCESS) + Status = EBlock(ServiceStrings, FileHeader.ServiceStringSize); + + if (Status == STATUS_SUCCESS) + Status = EBlock(Servers, FileHeader.ServerTableSize); + + if (Status == STATUS_SUCCESS) + Status = EBlock(ServerStrings, FileHeader.ServerStringSize); + + if (Status == STATUS_SUCCESS) + Status = EBlock(ServerServices, FileHeader.ServerServiceTableSize); + + if (Status == STATUS_SUCCESS) + Status = EBlock(Users, FileHeader.UserTableSize); + + if (Status == STATUS_SUCCESS) + Status = EBlock(UserStrings, FileHeader.UserStringSize); + + if (Status != STATUS_SUCCESS) + goto LLSDataSaveExit; + + // + // Save out the header record + // + hFile = LlsFileInit(UserFileName, USER_FILE_VERSION, sizeof(LLS_DATA_FILE_HEADER) ); + if (hFile == NULL) { + Status = GetLastError(); + goto LLSDataSaveExit; + } + + // + // Now write out all the data blocks + // + ret = WriteFile(hFile, &FileHeader, sizeof(LLS_DATA_FILE_HEADER), &BytesWritten, NULL); + + if (ret && (Services != NULL) && (FileHeader.ServiceTableSize != 0) ) + ret = WriteFile(hFile, Services, FileHeader.ServiceTableSize, &BytesWritten, NULL); + + if (ret && (ServiceStrings != NULL) && (FileHeader.ServiceStringSize != 0) ) + ret = WriteFile(hFile, ServiceStrings, FileHeader.ServiceStringSize, &BytesWritten, NULL); + + if (ret && (Servers != NULL) && (FileHeader.ServerTableSize != 0) ) + ret = WriteFile(hFile, Servers, FileHeader.ServerTableSize, &BytesWritten, NULL); + + if (ret && (ServerStrings != NULL) && (FileHeader.ServerStringSize != 0) ) + ret = WriteFile(hFile, ServerStrings, FileHeader.ServerStringSize, &BytesWritten, NULL); + + if (ret && (ServerServices != NULL) && (FileHeader.ServerServiceTableSize != 0) ) + ret = WriteFile(hFile, ServerServices, FileHeader.ServerServiceTableSize, &BytesWritten, NULL); + + if (ret && (Users != NULL) && (FileHeader.UserTableSize != 0) ) + ret = WriteFile(hFile, Users, FileHeader.UserTableSize, &BytesWritten, NULL); + + if (ret && (UserStrings != NULL) && (FileHeader.UserStringSize != 0) ) + ret = WriteFile(hFile, UserStrings, FileHeader.UserStringSize, &BytesWritten, NULL); + + if (!ret) + Status = GetLastError(); + +LLSDataSaveExit: + + if (hFile != NULL) + CloseHandle(hFile); + + // + // Run through our tables and clean them up + // + if (Services != NULL) + MIDL_user_free(Services); + + if (ServiceStrings != NULL) + MIDL_user_free(ServiceStrings); + + if (Servers != NULL) + MIDL_user_free(Servers); + + if (ServerStrings != NULL) + MIDL_user_free(ServerStrings); + + if (ServerServices != NULL) + MIDL_user_free(ServerServices); + + if (Users != NULL) + MIDL_user_free(Users); + + if (UserStrings != NULL) + MIDL_user_free(UserStrings); + + // + // If there was an error log it. + // + if (Status != STATUS_SUCCESS) + LogEvent(LLS_EVENT_SAVE_USER, 0, NULL, Status); + + return Status; +} // LLSDataSave + + +///////////////////////////////////////////////////////////////////////// +VOID +LoadAll ( ) + +/*++ + +Routine Description: + + +Arguments: + +Return Value: + + +--*/ + +{ +#if DBG + if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_DATABASE)) + dprintf(TEXT("LLS TRACE: LoadAll\n")); +#endif + + PurchaseFile = NULL; + LicenseListLoad(); + + MappingListLoad(); + LLSDataLoad(); + + CertDbLoad(); + +} // LoadAll + + +///////////////////////////////////////////////////////////////////////// +VOID +SaveAll ( ) + +/*++ + +Routine Description: + + +Arguments: + +Return Value: + + +--*/ + +{ +#if DBG + if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_DATABASE)) + dprintf(TEXT("LLS TRACE: SaveAll\n")); +#endif + + LicenseListSave(); + MappingListSave(); + LLSDataSave(); + CertDbSave(); + +} // SaveAll diff --git a/private/net/svcdlls/lls/server/pack.h b/private/net/svcdlls/lls/server/pack.h new file mode 100644 index 000000000..7e6feadeb --- /dev/null +++ b/private/net/svcdlls/lls/server/pack.h @@ -0,0 +1,195 @@ +/*++ + +Copyright (c) 1995 Microsoft Corporation + +Module Name: + + Pack.h + +Abstract: + + +Author: + + Arthur Hanson (arth) Dec 07, 1994 + +Environment: + +Revision History: + + Jeff Parham (jeffparh) 05-Dec-1995 + o Added new fields to purchase record to support secure certificates. + o Unified per server purchase model with per seat purchase model for + secure certificates; per server model still done in the traditional + manner for non-secure certificates (for backwards compatibility). + o Added SaveAll() function analogous to LoadAll(). + o Added support for extended user data packing/unpacking. This was + done to save the SUITE_USE flag across restarts of the service. + o Removed user table parameters from unpack routines that didn't use + them. + +--*/ + +#ifndef _LLS_PACK_H +#define _LLS_PACK_H + + +#ifdef __cplusplus +extern "C" { +#endif + +///////////////////////////////////////////////////////////////////// +// +// Save / Load Mapping +// +#define MAPPING_FILE_VERSION 0x0100 + +typedef struct _PACK_MAPPING_RECORD { + LPTSTR Name; + LPTSTR Comment; + ULONG Licenses; +} PACK_MAPPING_RECORD, *PPACK_MAPPING_RECORD; + +typedef struct _PACK_MAPPING_USER_RECORD { + ULONG Mapping; + LPTSTR Name; +} PACK_MAPPING_USER_RECORD, *PPACK_MAPPING_USER_RECORD; + +typedef struct _MAPPING_FILE_HEADER { + ULONG MappingUserTableSize; + ULONG MappingUserStringSize; + ULONG MappingTableSize; + ULONG MappingStringSize; +} MAPPING_FILE_HEADER, *PMAPPING_FILE_HEADER; + + +///////////////////////////////////////////////////////////////////// +// +// Save / Load License +// + +///////////////// OLD (3.51) FORMAT //////////////////// +#define LICENSE_FILE_VERSION_0 0x0100 + +typedef struct _PACK_LICENSE_PURCHASE_RECORD_0 { + ULONG Service; + LONG NumberLicenses; + DWORD Date; + LPTSTR Admin; + LPTSTR Comment; +} PACK_LICENSE_PURCHASE_RECORD_0, *PPACK_LICENSE_PURCHASE_RECORD_0; + +typedef struct _LICENSE_FILE_HEADER_0 { + ULONG LicenseServiceTableSize; + ULONG LicenseServiceStringSize; + ULONG LicenseTableSize; + ULONG LicenseStringSize; +} LICENSE_FILE_HEADER_0, *PLICENSE_FILE_HEADER_0; + +///////////////// NEW FORMAT //////////////////// +#define LICENSE_FILE_VERSION 0x0201 + +typedef struct _PACK_LICENSE_SERVICE_RECORD { + LPTSTR ServiceName; + LONG NumberLicenses; +} PACK_LICENSE_SERVICE_RECORD, *PPACK_LICENSE_SERVICE_RECORD; + +typedef struct _PACK_LICENSE_PURCHASE_RECORD { + ULONG Service; + LONG NumberLicenses; + DWORD Date; + LPTSTR Admin; + LPTSTR Comment; + + // new for SUR: (see description in purchase.h) + ULONG PerServerService; + DWORD AllowedModes; + DWORD CertificateID; + LPTSTR Source; + DWORD ExpirationDate; + DWORD MaxQuantity; + LPTSTR Vendor; + DWORD Secrets[ LLS_NUM_SECRETS ]; +} PACK_LICENSE_PURCHASE_RECORD, *PPACK_LICENSE_PURCHASE_RECORD; + +typedef struct _LICENSE_FILE_HEADER { + ULONG LicenseServiceTableSize; + ULONG LicenseServiceStringSize; + + ULONG LicenseTableSize; + ULONG LicenseStringSize; + + // new for SUR: + ULONG PerServerLicenseServiceTableSize; + ULONG PerServerLicenseServiceStringSize; + +} LICENSE_FILE_HEADER, *PLICENSE_FILE_HEADER; + + +///////////////////////////////////////////////////////////////////// +// +// Save / Load LLS Data +// + +///////////////// OLD (3.51) FORMAT //////////////////// +#define USER_FILE_VERSION_0 0x0100 + +typedef struct _LLS_DATA_FILE_HEADER_0 { + ULONG ServiceTableSize; + ULONG ServiceStringSize; + ULONG ServerTableSize; + ULONG ServerStringSize; + ULONG ServerServiceTableSize; + ULONG UserTableSize; + ULONG UserStringSize; +} LLS_DATA_FILE_HEADER_0, *PLLS_DATA_FILE_HEADER_0; + +///////////////// NEW FORMAT //////////////////// +#define USER_FILE_VERSION 0x0200 + +typedef struct _LLS_DATA_FILE_HEADER { + ULONG ServiceLevel; + ULONG ServiceTableSize; + ULONG ServiceStringSize; + + ULONG ServerLevel; + ULONG ServerTableSize; + ULONG ServerStringSize; + + ULONG ServerServiceLevel; + ULONG ServerServiceTableSize; + + ULONG UserLevel; + ULONG UserTableSize; + ULONG UserStringSize; +} LLS_DATA_FILE_HEADER, *PLLS_DATA_FILE_HEADER; + + + +VOID LicenseListLoad(); +NTSTATUS LicenseListSave(); +VOID MappingListLoad(); +NTSTATUS MappingListSave(); +VOID LLSDataLoad(); +NTSTATUS LLSDataSave(); + +VOID LoadAll ( ); +VOID SaveAll ( ); + +NTSTATUS ServiceListPack ( ULONG *pServiceTableSize, PREPL_SERVICE_RECORD *pServices ); +VOID ServiceListUnpack ( ULONG ServiceTableSize, PREPL_SERVICE_RECORD Services, ULONG ServerTableSize, PREPL_SERVER_RECORD Servers, ULONG ServerServiceTableSize, PREPL_SERVER_SERVICE_RECORD ServerServices ); +NTSTATUS ServerListPack ( ULONG *pServerTableSize, PREPL_SERVER_RECORD *pServers ); +VOID ServerListUnpack ( ULONG ServiceTableSize, PREPL_SERVICE_RECORD Services, ULONG ServerTableSize, PREPL_SERVER_RECORD Servers, ULONG ServerServiceTableSize, PREPL_SERVER_SERVICE_RECORD ServerServices ); +NTSTATUS ServerServiceListPack ( ULONG *pServerServiceTableSize, PREPL_SERVER_SERVICE_RECORD *pServerServices ); +VOID ServerServiceListUnpack ( ULONG ServiceTableSize, PREPL_SERVICE_RECORD Services, ULONG ServerTableSize, PREPL_SERVER_RECORD Servers, ULONG ServerServiceTableSize, PREPL_SERVER_SERVICE_RECORD ServerServices ); +NTSTATUS UserListPack ( DWORD LastReplicated, ULONG UserLevel, ULONG *pUserTableSize, LPVOID *pUsers ); +VOID UserListUnpack ( ULONG ServiceTableSize, PREPL_SERVICE_RECORD Services, ULONG ServerTableSize, PREPL_SERVER_RECORD Servers, ULONG ServerServiceTableSize, PREPL_SERVER_SERVICE_RECORD ServerServices, ULONG UserLevel, ULONG UserTableSize, LPVOID Users ); +NTSTATUS PackAll ( DWORD LastReplicated, ULONG *pServiceTableSize, PREPL_SERVICE_RECORD *pServices, ULONG *pServerTableSize, PREPL_SERVER_RECORD *pServers, ULONG *pServerServiceTableSize, PREPL_SERVER_SERVICE_RECORD *pServerServices, ULONG UserLevel, ULONG *pUserTableSize, LPVOID *pUsers ); +VOID UnpackAll ( ULONG ServiceTableSize, PREPL_SERVICE_RECORD Services, ULONG ServerTableSize, PREPL_SERVER_RECORD Servers, ULONG ServerServiceTableSize, PREPL_SERVER_SERVICE_RECORD ServerServices, ULONG UserLevel, ULONG UserTableSize, LPVOID Users ); + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/private/net/svcdlls/lls/server/perseat.c b/private/net/svcdlls/lls/server/perseat.c new file mode 100644 index 000000000..3bb7c4333 --- /dev/null +++ b/private/net/svcdlls/lls/server/perseat.c @@ -0,0 +1,3351 @@ +/*++ + +Copyright (c) 1995 Microsoft Corporation + +Module Name: + + perseat.c + +Abstract: + + Routines to handle per-seat licensing. Handles the in-memory cache + of useage via the Rtl Generic table functions (these are a generic + splay tree package). + + There can be up to three tables kept. The first table is a username + table and is the main table. The second table is for SID's, which will + be converted into usernames when replicated. + + The SID and username trees are handled in this module as they are used + by all modes of the server. + +Author: + + Arthur Hanson (arth) 03-Jan-1995 + +Revision History: + + Jeff Parham (jeffparh) 12-Jan-1996 + o Fixed possible infinite loop in UserListLicenseDelete(). + o In FamilyLicenseUpdate(), now rescans for BackOffice upgrades + regardless of whether the family being updated was BackOffice. + This fixes a problem wherein a freed BackOffice license was + not being assigned to a user that needed it. (Bug #3299.) + o Added support for maintaining the SUITE_USE flag when adding + users to the AddCache. + +--*/ + +#include <stdlib.h> +#include <nt.h> +#include <ntrtl.h> +#include <nturtl.h> +#include <windows.h> + +#include "debug.h" +#include "llsutil.h" +#include "llssrv.h" +#include "mapping.h" +#include "msvctbl.h" +#include "svctbl.h" +#include "perseat.h" +#include "llsevent.h" + +#define NO_LLS_APIS +#include "llsapi.h" + +// +// At what # of product do we switch to BackOffice +// +#define BACKOFFICE_SWITCH 3 + +///////////////////////////////////////////////////////////////////////// +// +// Actual User and SID Lists, and their access locks +// +ULONG UserListNumEntries = 0; +ULONG SidListNumEntries = 0; +RTL_GENERIC_TABLE UserList; +RTL_GENERIC_TABLE SidList; + +RTL_RESOURCE UserListLock; +RTL_RESOURCE SidListLock; + +///////////////////////////////////////////////////////////////////////// +// +// The enum processes for replication and UI can take awhile to go through +// all the records, while doing this they need a shared lock on the file. +// However, if we request an exclusive access during this time the pending +// exclusive access will block other shared access's. Therefore we get +// this lock first before attempting either an add or enum. +// +// An add will block enums, but neither of these function are as time +// critical as updating normal user records. +// +RTL_RESOURCE UserListAddEnumLock; +RTL_RESOURCE SidListAddEnumLock; + +///////////////////////////////////////////////////////////////////////// +// +// The AddCache itself, a critical section to protect access to it and an +// event to signal the server when there are items on it that need to be +// processed. +// +PADD_CACHE AddCache = NULL; +ULONG AddCacheSize = 0; +RTL_CRITICAL_SECTION AddCacheLock; +HANDLE LLSAddCacheEvent; + +DWORD LastUsedTime = 0; +BOOL UsersDeleted = FALSE; + + +RTL_CRITICAL_SECTION GenTableLock; + +///////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////// +// +// The License List is a linear list of all the licenses the object is +// using. +// +// The license list is kept as part of each user and mapping record, if +// the user is mapped then the mapping should contain the license list. +// The structure is a sorted array of pointers to License Records, and +// access is controled by the ServiceTableLock. +// +// The license is identified by the Service Family Name (the license list +// is sorted on this). +// + +///////////////////////////////////////////////////////////////////////// +int __cdecl +LicenseListCompare(const void *arg1, const void *arg2) { + PUSER_LICENSE_RECORD pLic1, pLic2; + + pLic1 = (PUSER_LICENSE_RECORD) *((PUSER_LICENSE_RECORD *) arg1); + pLic2 = (PUSER_LICENSE_RECORD) *((PUSER_LICENSE_RECORD *) arg2); + + return lstrcmpi( pLic1->Family->Name, pLic2->Family->Name ); + +} // LicenseListCompare + + +///////////////////////////////////////////////////////////////////////// +PUSER_LICENSE_RECORD +LicenseListFind( + LPTSTR Name, + PUSER_LICENSE_RECORD *pLicenseList, + ULONG NumTableEntries + ) + +/*++ + +Routine Description: + + Find the license in a license list for the given Family of products. + +Arguments: + + Name - Name of product family to find license for. + + pLicenseList - Size of the license list to search. + + NumTableEntries - Pointer to the license List to search. + +Return Value: + + Pointer to the found License Record, or NULL if not found. + +--*/ + +{ + LONG begin = 0; + LONG end = (LONG) NumTableEntries - 1; + LONG cur; + int match; + +#if DBG + if (TraceFlags & TRACE_FUNCTION_TRACE) + dprintf(TEXT("LLS TRACE: LicenseListFind\n")); +#endif + + if ((Name == NULL) || (pLicenseList == NULL) || (NumTableEntries == 0)) + return NULL; + + while (end >= begin) { + // go halfway in-between + cur = (begin + end) / 2; + + // compare the two result into match + match = lstrcmpi(Name, pLicenseList[cur]->Family->Name); + + if (match < 0) + // move new begin + end = cur - 1; + else + begin = cur + 1; + + if (match == 0) + return pLicenseList[cur]; + } + + return NULL; + +} // LicenseListFind + + +///////////////////////////////////////////////////////////////////////// +NTSTATUS +LicenseListDelete( + PMASTER_SERVICE_ROOT Family, + PUSER_LICENSE_RECORD **pLicenses, + PULONG pLicenseListSize + ) + +/*++ + +Routine Description: + + Delete the given license from the license list. + +Arguments: + + Family - + + pLicenses - + + pLicenseListSize - + +Return Value: + + STATUS_SUCCESS if successful, else error code. + +--*/ + +{ + PUSER_LICENSE_RECORD *LicenseList; + ULONG LicenseListSize; + PUSER_LICENSE_RECORD LicenseRec; + ULONG i; + +#if DBG + if (TraceFlags & TRACE_FUNCTION_TRACE) + dprintf(TEXT("LLS TRACE: LicenseListDelete\n")); +#endif + + if ( (pLicenses == NULL) || (pLicenseListSize == NULL) ) + return STATUS_OBJECT_NAME_NOT_FOUND; + + LicenseListSize = *pLicenseListSize; + LicenseList = *pLicenses; + + // + // Get record based on name given + // + LicenseRec = LicenseListFind(Family->Name, LicenseList, LicenseListSize); + if (LicenseRec == NULL) + return STATUS_OBJECT_NAME_NOT_FOUND; + + // + // Check if this is the last user + // + if (LicenseListSize == 1) { + LocalFree(LicenseList); + *pLicenseListSize = 0; + *pLicenses = NULL; + return STATUS_SUCCESS; + } + + // + // Not the last so find it in the list + // + i = 0; + while ( (i < LicenseListSize) && (LicenseList[i]->Family != Family) ) + i++; + + // + // Now move everything below it up. + // + i++; + while (i < LicenseListSize) { + LicenseList[i-1] = LicenseList[i]; + i++; + } + + LicenseList = (PUSER_LICENSE_RECORD *) LocalReAlloc(LicenseList, sizeof(PUSER_LICENSE_RECORD) * (LicenseListSize - 1), LHND); + + // + // Make sure we could allocate table + // + ASSERT(LicenseList != NULL); + if (LicenseList == NULL) + LicenseListSize = 0; + else + LicenseListSize--; + + LocalFree(LicenseRec); + *pLicenses = LicenseList; + *pLicenseListSize = LicenseListSize; + + return STATUS_SUCCESS; + +} // LicenseListDelete + + +///////////////////////////////////////////////////////////////////////// +PUSER_LICENSE_RECORD +LicenseListAdd( + PMASTER_SERVICE_ROOT Family, + PUSER_LICENSE_RECORD **pLicenses, + PULONG pLicenseListSize + ) + +/*++ + +Routine Description: + + Adds an empty license record to the license list. Sets the license + family, but not any of the other info. + +Arguments: + + +Return Value: + + +--*/ + +{ + PUSER_LICENSE_RECORD *LicenseList; + ULONG LicenseListSize; + PUSER_LICENSE_RECORD LicenseRec; + +#if DBG + if (TraceFlags & TRACE_FUNCTION_TRACE) + dprintf(TEXT("LLS TRACE: LicenseListAdd\n")); +#endif + + if ((Family == NULL) || (pLicenses == NULL) || (pLicenseListSize == NULL) ) + return NULL; + + LicenseList = *pLicenses; + LicenseListSize = *pLicenseListSize; + + // + // We do a double check here to see if another thread just got done + // adding the Mapping, between when we checked last and actually got + // the write lock. + // + LicenseRec = LicenseListFind(Family->Name, LicenseList, LicenseListSize ); + + if (LicenseRec != NULL) { + return LicenseRec; + } + + // + // Allocate space for table (zero init it). + // + if (LicenseList == NULL) + LicenseList = (PUSER_LICENSE_RECORD *) LocalAlloc(LPTR, sizeof(PUSER_LICENSE_RECORD)); + else + LicenseList = (PUSER_LICENSE_RECORD *) LocalReAlloc(LicenseList, sizeof(PUSER_LICENSE_RECORD) * (LicenseListSize + 1), LHND); + + // + // Make sure we could allocate Mapping table + // + if (LicenseList == NULL) { + pLicenses = NULL; + pLicenseListSize = 0; + return NULL; + } + + LicenseRec = (PUSER_LICENSE_RECORD) LocalAlloc(LPTR, sizeof(USER_LICENSE_RECORD)); + if (LicenseRec == NULL) + return NULL; + + // now copy it over... + LicenseList[LicenseListSize] = LicenseRec; + LicenseRec->Family = Family; + LicenseRec->Flags = LLS_FLAG_LICENSED; + LicenseRec->RefCount = 0; + LicenseRec->Service = NULL; + LicenseRec->LicensesNeeded = 0; + + LicenseListSize++; + + // Have added the entry - now need to sort it in order of the names + qsort((void *) LicenseList, (size_t) LicenseListSize, sizeof(PUSER_LICENSE_RECORD), LicenseListCompare); + + *pLicenses = LicenseList; + *pLicenseListSize = LicenseListSize; + return LicenseRec; + +} // LicenseListAdd + + +///////////////////////////////////////////////////////////////////////// +// These routines are specific to the license list in the user and +// mapping records. +///////////////////////////////////////////////////////////////////////// + +///////////////////////////////////////////////////////////////////////// +VOID +UserLicenseListFree ( + PUSER_RECORD pUser + ) + +/*++ + +Routine Description: + + Walks the license list deleting all entries and freeing up any claimed + licenses from the service table. This only cleans up the licenses + in a user record (not a mapping) so the # licenses is always 1. + +Arguments: + + +Return Value: + + +--*/ + +{ + ULONG i; + BOOL ReScan = FALSE; + +#if DBG + if (TraceFlags & TRACE_FUNCTION_TRACE) + dprintf(TEXT("LLS TRACE: UserLicenseListFree\n")); +#endif + + // + // Walk license table and free all licenses + // + for (i = 0; i < pUser->LicenseListSize; i++) { + pUser->LicenseList[i]->Service->LicensesUsed -= 1; + pUser->LicenseList[i]->Service->LicensesClaimed -= (1 - pUser->LicenseList[i]->LicensesNeeded); + pUser->LicenseList[i]->Service->Family->Flags |= LLS_FLAG_UPDATE; + ReScan = TRUE; + LocalFree(pUser->LicenseList[i]); + } + + // + // Free related entries in user list + // + if (pUser->LicenseList != NULL) + LocalFree(pUser->LicenseList); + + pUser->LicenseList = NULL; + pUser->LicenseListSize = 0; + pUser->LicensedProducts = 0; + + // + // Get rid of pointers in services table + // + for (i = 0; i < pUser->ServiceTableSize; i++) + pUser->Services[i].License = NULL; + + // + // Check if we freed up licenses and need to re-scan the user-table + // + if (ReScan) { + // + // Set to licensed so scan doesn't assign to ourself + // + pUser->Flags |= LLS_FLAG_LICENSED; + + for (i = 0; i < RootServiceListSize; i++) { + if (RootServiceList[i]->Flags & LLS_FLAG_UPDATE) { + RootServiceList[i]->Flags &= ~LLS_FLAG_UPDATE; + FamilyLicenseUpdate( RootServiceList[i] ); + } + } + + if (pUser->ServiceTableSize > 0) + pUser->Flags &= ~LLS_FLAG_LICENSED; + } +} // UserLicenseListFree + + +///////////////////////////////////////////////////////////////////////// +VOID +MappingLicenseListFree ( + PMAPPING_RECORD pMap + ) + +/*++ + +Routine Description: + + Walks the license list in a mapping freeing up any claimed licenses. + Like UserLicenseListFree, but for a mapping. + +Arguments: + + +Return Value: + + +--*/ + +{ + ULONG i; + BOOL ReScan = FALSE; + +#if DBG + if (TraceFlags & TRACE_FUNCTION_TRACE) + dprintf(TEXT("LLS TRACE: MappingLicenseListFree\n")); +#endif + + // + // Walk license table and free all licenses + // + for (i = 0; i < pMap->LicenseListSize; i++) { + pMap->LicenseList[i]->Service->LicensesUsed -= pMap->Licenses; + pMap->LicenseList[i]->Service->LicensesClaimed -= (pMap->Licenses - pMap->LicenseList[i]->LicensesNeeded); + pMap->LicenseList[i]->Service->Family->Flags |= LLS_FLAG_UPDATE; + ReScan = TRUE; + LocalFree(pMap->LicenseList[i]); + } + + // + // Free related entries in mapping list + // + if (pMap->LicenseList != NULL) + LocalFree(pMap->LicenseList); + + pMap->LicenseList = NULL; + pMap->LicenseListSize = 0; + + // + // Check if we freed up licenses and need to re-scan the user-table + // + if (ReScan) + for (i = 0; i < RootServiceListSize; i++) { + if (RootServiceList[i]->Flags & LLS_FLAG_UPDATE) { + RootServiceList[i]->Flags &= ~LLS_FLAG_UPDATE; + FamilyLicenseUpdate( RootServiceList[i] ); + } + } + +} // MappingLicenseListFree + + + +///////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////// +// +// The service table is a linear array of records pointed to by the +// user record. Each entry contains a pointer into the service table +// identifying the service, some statistical useage information and a +// pointer into the license table identifying the license used by the +// service. +// + +///////////////////////////////////////////////////////////////////////// +int __cdecl +SvcListCompare( + const void *arg1, + const void *arg2 + ) +{ + PSVC_RECORD pSvc1, pSvc2; + + pSvc1 = (PSVC_RECORD) arg1; + pSvc2 = (PSVC_RECORD) arg2; + + return lstrcmpi( pSvc1->Service->Name, pSvc2->Service->Name ); + +} // SvcListCompare + + +///////////////////////////////////////////////////////////////////////// +PSVC_RECORD +SvcListFind( + LPTSTR DisplayName, + PSVC_RECORD ServiceList, + ULONG NumTableEntries + ) + +/*++ + +Routine Description: + + Internal routine to actually do binary search on Service List in user + record. This is a binary search, however since the string pointers are + from the service table and therefore the pointers are fixed, we only + need to compare the pointers, not the strings themselves to find a + match. + +Arguments: + + ServiceName - + +Return Value: + + Pointer to found service table entry or NULL if not found. + +--*/ + +{ + LONG begin = 0; + LONG end = (LONG) NumTableEntries - 1; + LONG cur; + int match; + +#if DBG + if (TraceFlags & TRACE_FUNCTION_TRACE) + dprintf(TEXT("LLS TRACE: SvcListFind\n")); +#endif + if ((DisplayName == NULL) || (ServiceList == NULL) || (NumTableEntries == 0)) + return NULL; + + while (end >= begin) { + // go halfway in-between + cur = (begin + end) / 2; + + // compare the two result into match + match = lstrcmpi(DisplayName, ServiceList[cur].Service->Name); + + if (match < 0) + // move new begin + end = cur - 1; + else + begin = cur + 1; + + if (match == 0) + return &ServiceList[cur]; + } + + return NULL; + +} // SvcListFind + + +///////////////////////////////////////////////////////////////////////// +NTSTATUS +SvcListDelete( + LPTSTR UserName, + LPTSTR ServiceName +) + +/*++ + +Routine Description: + + Deletes a service record from the service table. + +Arguments: + + +Return Value: + + +--*/ + +{ + PUSER_RECORD pUserRec; + PSVC_RECORD pService; + PSVC_RECORD SvcTable = NULL; + PUSER_LICENSE_RECORD License = NULL; + ULONG NumLicenses = 1; + ULONG i; + BOOL ReScan = FALSE; + PMASTER_SERVICE_ROOT Family = NULL; + +#if DBG + if (TraceFlags & TRACE_FUNCTION_TRACE) + dprintf(TEXT("LLS TRACE: SvcListDelete\n")); +#endif + + pUserRec = UserListFind(UserName); + if (pUserRec == NULL) + return STATUS_OBJECT_NAME_NOT_FOUND; + + RtlEnterCriticalSection(&pUserRec->ServiceTableLock); + pService = SvcListFind( ServiceName, pUserRec->Services, pUserRec->ServiceTableSize ); + + // + // If we couldn't find it then get out. + // + if (pService == NULL) { + RtlLeaveCriticalSection(&pUserRec->ServiceTableLock); + return STATUS_OBJECT_NAME_NOT_FOUND; + } + + Family = pService->Service->Family; + + // + // If we are a mapping then we may use more then one license + // + if (pUserRec->Mapping != NULL) + NumLicenses = pUserRec->Mapping->Licenses; + + License = pService->License; + + if (License != NULL) { + License->RefCount--; + + // + // If this is the last service that uses this license then we need + // to get rid of it. + // + if (License->RefCount == 0) { + License->Service->LicensesUsed -= NumLicenses; + NumLicenses -= License->LicensesNeeded; + License->Service->LicensesClaimed -= NumLicenses; + + // + // Do we need to delete it from the mapping or user license table? + // + if (pUserRec->Mapping != NULL) { + if ((License->Service == BackOffice) && (pUserRec->Mapping->Flags & LLS_FLAG_SUITE_AUTO)) + pUserRec->Mapping->Flags &= ~LLS_FLAG_SUITE_USE; + + LicenseListDelete(License->Service->Family, &pUserRec->Mapping->LicenseList, &pUserRec->Mapping->LicenseListSize ); + + } else { + if ((License->Service == BackOffice) && (pUserRec->Flags & LLS_FLAG_SUITE_AUTO)) + pUserRec->Flags &= ~LLS_FLAG_SUITE_USE; + + LicenseListDelete(License->Service->Family, &pUserRec->LicenseList, &pUserRec->LicenseListSize ); + } + + // + // Freed a license so need to scan and adjust counts + // + ReScan = TRUE; + } + } + + if (pService->Flags & LLS_FLAG_LICENSED) + pUserRec->LicensedProducts--; + else { + // + // This was an unlicensed product - see if this makes the user + // licensed + // + if (pUserRec->LicensedProducts == (pUserRec->ServiceTableSize - 1)) + pUserRec->Flags |= LLS_FLAG_LICENSED; + } + + // + // First check if this is the only entry in the table + // + if (pUserRec->ServiceTableSize == 1) { + LocalFree(pUserRec->Services); + pUserRec->Services = NULL; + goto SvcListDeleteExit; + } + + // + // Find this record linearly in the table. + // + i = 0; + while ((i < pUserRec->ServiceTableSize) && (lstrcmpi(pUserRec->Services[i].Service->Name, ServiceName))) + i++; + + // + // Now move everything below it up. + // + i++; + while (i < pUserRec->ServiceTableSize) { + memcpy(&pUserRec->Services[i-1], &pUserRec->Services[i], sizeof(SVC_RECORD)); + i++; + } + + SvcTable = (PSVC_RECORD) LocalReAlloc( pUserRec->Services, sizeof(SVC_RECORD) * (pUserRec->ServiceTableSize - 1), LHND); + + ASSERT(SvcTable != NULL); + if (SvcTable == NULL) { + pUserRec->Services = NULL; + pUserRec->ServiceTableSize = 0; + RtlLeaveCriticalSection(&pUserRec->ServiceTableLock); + return STATUS_SUCCESS; + } + + pUserRec->Services = SvcTable; + +SvcListDeleteExit: + pUserRec->ServiceTableSize--; + + if (pUserRec->ServiceTableSize == 0) + pUserRec->Services = NULL; + + RtlLeaveCriticalSection(&pUserRec->ServiceTableLock); + + if (ReScan) + FamilyLicenseUpdate ( Family ); + + return STATUS_SUCCESS; + +} // SvcListDelete + + +///////////////////////////////////////////////////////////////////////// +VOID +SvcListLicenseFree( + PUSER_RECORD pUser +) + +/*++ + +Routine Description: + + + Walk the services table and free up any licenses they are using. If the + licenses are then no longer needed (refCount == 0) then the license is + deleted. + +Arguments: + + +Return Value: + + +--*/ + +{ + ULONG i; + ULONG NumLicenses = 1; + PUSER_LICENSE_RECORD License = NULL; + BOOL ReScan = FALSE; + +#if DBG + if (TraceFlags & TRACE_FUNCTION_TRACE) + dprintf(TEXT("LLS TRACE: SvcListLicenseFree\n")); +#endif + + // + // If we are a mapping then we may use more then one license + // + for (i = 0; i < pUser->ServiceTableSize; i++) { + + if (pUser->Mapping != NULL) + NumLicenses = pUser->Mapping->Licenses; + else + NumLicenses = 1; + + License = pUser->Services[i].License; + + if (License != NULL) { + License->RefCount--; + + // + // If this is the last service that uses this license then we need + // to get rid of it. + // + if (License->RefCount == 0) { + if ( (pUser->Mapping != NULL) && (License->Service == BackOffice) && (pUser->Mapping->Flags & LLS_FLAG_SUITE_AUTO) ) + pUser->Mapping->Flags &= ~LLS_FLAG_SUITE_USE; + + License->Service->LicensesUsed -= NumLicenses; + NumLicenses -= License->LicensesNeeded; + + if (License->Service->LicensesClaimed > 0) { + // + // Freed a license so need to scan and adjust counts + // + License->Service->Family->Flags |= LLS_FLAG_UPDATE; + ReScan = TRUE; + } + + License->Service->LicensesClaimed -= NumLicenses; + + if (pUser->Mapping != NULL) + LicenseListDelete(License->Service->Family, &pUser->Mapping->LicenseList, &pUser->Mapping->LicenseListSize ); + else + LicenseListDelete(License->Service->Family, &pUser->LicenseList, &pUser->LicenseListSize ); + + } + } + + pUser->Services[i].License = NULL; + } + + pUser->LicensedProducts = 0; + + // + // Check if we freed up licenses and need to re-scan the user-table + // + if (ReScan) { + // + // Flag license so rescan won't worry about this user + // + pUser->Flags |= LLS_FLAG_LICENSED; + + for (i = 0; i < RootServiceListSize; i++) { + if (RootServiceList[i]->Flags & LLS_FLAG_UPDATE) { + RootServiceList[i]->Flags &= ~LLS_FLAG_UPDATE; + FamilyLicenseUpdate( RootServiceList[i] ); + } + } + } + +} // SvcListLicenseFree + + +///////////////////////////////////////////////////////////////////////// +VOID +SvcListLicenseUpdate( + PUSER_RECORD pUser +) + +/*++ + +Routine Description: + + Walk the services table and assign the appropriate license to each + service. This is the opposite of the SvcListLicenseFree Routine. + +Arguments: + + +Return Value: + + +--*/ + +{ + ULONG i; + ULONG Claimed = 0; + PUSER_LICENSE_RECORD License = NULL; + +#if DBG + if (TraceFlags & TRACE_FUNCTION_TRACE) + dprintf(TEXT("LLS TRACE: SvcListLicenseUpdate\n")); +#endif + + // + // Check if user is set to use BackOffice + if ( pUser->Flags & LLS_FLAG_SUITE_USE ) { + // + // Go grab a backoffice license to fulfill the suite useage + // + License = LicenseListAdd(BackOffice->Family, &pUser->LicenseList, &pUser->LicenseListSize); + + ASSERT(License != NULL); + if (License != NULL) { + License->Service = BackOffice; + RtlAcquireResourceExclusive(&MasterServiceListLock, TRUE); + + // Can only claim a # of licenses that we have + if ( BackOffice->LicensesClaimed < BackOffice->Licenses) { + Claimed = BackOffice->Licenses - BackOffice->LicensesClaimed; + + if (Claimed > 1) + Claimed = 1; + + } + + // + // Adjust license counts + // + BackOffice->LicensesUsed += 1; + BackOffice->LicensesClaimed += Claimed; + License->LicensesNeeded = 1 - Claimed; + + // + // Figure out if we are licensed or not. + // + if (License->LicensesNeeded > 0) { + // + // Not licensed + // + License->Flags &= ~LLS_FLAG_LICENSED; + pUser->Flags &= ~LLS_FLAG_LICENSED; + + for (i = 0; i < pUser->ServiceTableSize; i++) { + pUser->Services[i].Flags &= ~LLS_FLAG_LICENSED; + pUser->Services[i].License = License; + License->RefCount++; + } + } else { + // + // Licensed + // + License->Flags |= LLS_FLAG_LICENSED; + pUser->Flags |= LLS_FLAG_LICENSED; + + for (i = 0; i < pUser->ServiceTableSize; i++) { + pUser->LicensedProducts++; + pUser->Services[i].Flags |= LLS_FLAG_LICENSED; + pUser->Services[i].License = License; + License->RefCount++; + } + } + + RtlReleaseResource(&MasterServiceListLock); + } + + } else { + BOOL Licensed = TRUE; + + // + // Loop through all the services and make sure they are all + // licensed. + // + for (i = 0; i < pUser->ServiceTableSize; i++) { + SvcLicenseUpdate(pUser, &pUser->Services[i]); + + if ( pUser->Services[i].Flags & LLS_FLAG_LICENSED ) + pUser->LicensedProducts++; + else + Licensed = FALSE; + } + + if (Licensed) + pUser->Flags |= LLS_FLAG_LICENSED; + else + pUser->Flags &= ~LLS_FLAG_LICENSED; + } + +} // SvcListLicenseUpdate + + +///////////////////////////////////////////////////////////////////////// +VOID +SvcLicenseUpdate( + PUSER_RECORD pUser, + PSVC_RECORD Svc +) + +/*++ + +Routine Description: + + For a given service record for a user check and update license compliance. + Is a single service record version of SvcListLicenseUpdate. + +Arguments: + + +Return Value: + + +--*/ + +{ + ULONG NumLicenses = 1; + PUSER_LICENSE_RECORD License = NULL; + BOOL UseMapping = FALSE; + PMASTER_SERVICE_RECORD LicenseService = NULL; + PMASTER_SERVICE_RECORD Service; + BOOL ReScan = FALSE; + DWORD Flags = 0; + +#if DBG + if (TraceFlags & TRACE_FUNCTION_TRACE) + dprintf(TEXT("LLS TRACE: SvcLicenseUpdate\n")); +#endif + + if ((pUser == NULL) || (Svc == NULL)) + return; + + Flags = pUser->Flags; + + // + // If we are a mapping then we may use more then one license + // + if (pUser->Mapping != NULL) { + NumLicenses = pUser->Mapping->Licenses; + UseMapping = TRUE; + Flags = pUser->Mapping->Flags; + } + + // + // Try to find a license record in the license list of the user or mapping + // to use. If we are using BackOffice then look for BackOffice license + // instead of the service license. + if (Flags & LLS_FLAG_SUITE_USE) { + Service = BackOffice; + + if (UseMapping) + License = LicenseListFind(BackOfficeStr, pUser->Mapping->LicenseList, pUser->Mapping->LicenseListSize); + else + License = LicenseListFind(BackOfficeStr, pUser->LicenseList, pUser->LicenseListSize); + + } else { + // + // Not BackOffice - so look for normal service license + // + Service = Svc->Service; + ASSERT(Service != NULL); + + // + // Try to find a license for this family of products + // + if (UseMapping) + License = LicenseListFind(Service->Family->Name, pUser->Mapping->LicenseList, pUser->Mapping->LicenseListSize); + else + License = LicenseListFind(Service->Family->Name, pUser->LicenseList, pUser->LicenseListSize); + } + + // + // Check if we couldn't find a license. If we didn't find it then we need + // to create a new license for this. + // + if (License == NULL) { + ULONG LicenseListSize; + PUSER_LICENSE_RECORD *LicenseList; + + // + // The license list to use depends if we are part of a mapping or not. + // + if (UseMapping) { + LicenseListSize = pUser->Mapping->LicenseListSize; + LicenseList = pUser->Mapping->LicenseList; + } else { + LicenseListSize = pUser->LicenseListSize; + LicenseList = pUser->LicenseList; + } + + // + // Check if we need to add a license for BackOffice or just the service + // itself. + // + if (Flags & LLS_FLAG_SUITE_USE) + License = LicenseListAdd(BackOffice->Family, &LicenseList, &LicenseListSize); + else + License = LicenseListAdd(Service->Family, &LicenseList, &LicenseListSize); + + // + // Now update the couters in the parent record + // + if (UseMapping) { + pUser->Mapping->LicenseListSize = LicenseListSize; + pUser->Mapping->LicenseList = LicenseList; + } else { + pUser->LicenseListSize = LicenseListSize; + pUser->LicenseList = LicenseList; + } + + if (License != NULL) + License->LicensesNeeded = NumLicenses; + } + + // + // We have either found an old license or added a new one, either way + // License points to it. + // + if (License != NULL) { + RtlAcquireResourceExclusive(&MasterServiceListLock, TRUE); + + // + // if we have a license for this family already and the product + // version >= current then we are okay, else need to get new license + // + if ( (License->Service != NULL) && (License->Service->Version >= Service->Version) ) { + LicenseService = License->Service; + } else { + // + // we have an old license for this family, but the version + // isn't adequate, so we need to try and get a new license. + // Walk the family tree looking for the licenses we + // need. + // + LicenseService = Service; + while ((LicenseService != NULL) && ( (LicenseService->LicensesUsed + NumLicenses) > LicenseService->Licenses) ) + if (LicenseService->next > 0) + LicenseService = MasterServiceTable[LicenseService->next - 1]; + else + LicenseService = NULL; + + // + // if we couldn't find a license just use the old + // service. + if (LicenseService == NULL) + LicenseService = Service; + else { + // + // Need to clean up old stuff + // + if (License->Service != NULL) { + // + // If we actually free up any licenses then mark that we need + // to rescan to allocate these freed licenses. + // + if ((NumLicenses - License->LicensesNeeded) > 0) + ReScan = TRUE; + + License->Service->LicensesUsed -= NumLicenses; + License->Service->LicensesClaimed -= (NumLicenses - License->LicensesNeeded); + License->LicensesNeeded = NumLicenses; + License->Service = NULL; + } + } + } + + if (LicenseService != NULL) { + ULONG Claimed = 0; + + // + // If we switched services then adjust LicensesUsed + // + if (License->Service != LicenseService) { + LicenseService->LicensesUsed += NumLicenses; + + if (License->Service != NULL) { + License->Service->LicensesUsed -= NumLicenses; + } + } + + // Can only claim a # of licenses that we have + if ( LicenseService->LicensesClaimed < LicenseService->Licenses) { + Claimed = LicenseService->Licenses - LicenseService->LicensesClaimed; + + if (Claimed > License->LicensesNeeded) + Claimed = License->LicensesNeeded; + + } + + LicenseService->LicensesClaimed += Claimed; + License->Service = LicenseService; + License->LicensesNeeded -= Claimed; + + if (License->LicensesNeeded != 0) + License->Flags &= ~LLS_FLAG_LICENSED; + else + License->Flags |= LLS_FLAG_LICENSED; + } + + RtlReleaseResource(&MasterServiceListLock); + + if (License->Flags & LLS_FLAG_LICENSED) + Svc->Flags |= LLS_FLAG_LICENSED; + else + Svc->Flags &= ~LLS_FLAG_LICENSED; + + } else + Svc->Flags &= ~LLS_FLAG_LICENSED; + + if (Svc->License != License) + License->RefCount++; + + Svc->License = License; + if (ReScan) + FamilyLicenseUpdate ( Service->Family ); + +} // SvcLicenseUpdate + + + +///////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////// +// +// Misc licensing Routines. +// +// BackOffice and Mappings have a special affect on LicenseUseage and so +// there are a couple miscelanous routines to handle them. +// +// There are also two special cases that cause us to re-walk our lists to +// fixup the licenses: +// +// 1. Sometimes when we add licenses we free up some we already had claimed. +// Ex: Users of a LicenseGroup used 5 SQL 4.0 licenses but could only +// claim 2 (there weren't enough). Later we add 5 SQL 5.0 licenses, +// since we can use these to get into license compliance we free the +// 2 previously claimed licenses and take the 5 SQL 5.0 licenses. Now +// we need to re-walk the user table to try and apply the 2 freed +// licenses. +// +// If we switch a user to BackOffice then it will also free up licenses +// causing us to re-walk the table. +// +// 2. If we ever apply new licenses to a user in a mapping then we need +// to re-walk all the other users in the mapping and update their +// license compliance. +// + +///////////////////////////////////////////////////////////////////////// +VOID +MappingLicenseUpdate ( + PMAPPING_RECORD Mapping, + BOOL ReSynch + ) + +/*++ + +Routine Description: + + Go through all user records for a given mapping and recalc license + compliance. + +Arguments: + + Mapping - the Mapping to recalc licenses for. + + ReSync - If true all previous licenses are destroyed before the licenses + are checked, else only services that currently don't have a + license assignment are touched. + +Return Value: + + +--*/ + +{ + ULONG i, j; + PUSER_LICENSE_RECORD License = NULL; + PUSER_RECORD pUser; + PSVC_RECORD SvcTable = NULL; + BOOL BackOfficeCheck = FALSE; + ULONG Claimed; + BOOL Licensed = TRUE; + +#if DBG + if (TraceFlags & TRACE_FUNCTION_TRACE) + dprintf(TEXT("LLS TRACE: MappingLicenseUpdate\n")); +#endif + + // + // Run through all the users in the mapping - adjust their licenses + // based on the licenses the mapping has... + // + RtlAcquireResourceExclusive(&MappingListLock, TRUE); + for (i = 0; i < Mapping->LicenseListSize; i++) + if (!(Mapping->LicenseList[i]->Flags & LLS_FLAG_LICENSED)) + Licensed = FALSE; + + if (Licensed) + Mapping->Flags |= LLS_FLAG_LICENSED; + else + Mapping->Flags &= ~LLS_FLAG_LICENSED; + + // + // If we want to resynch then blow away all old references + // + if (ReSynch) + for (i = 0; i < Mapping->LicenseListSize; i++) + Mapping->LicenseList[i]->RefCount = 0; + + // + // Special handling if the Mapping uses BackOffice + // + if (Mapping->Flags & LLS_FLAG_SUITE_USE) { + License = LicenseListFind(BackOfficeStr, Mapping->LicenseList, Mapping->LicenseListSize); + + // + // If there isn't one (can happen if all users were deleted from + // the mapping with BackOffice flag set). Then update everything. + // + if (License == NULL) { + License = LicenseListAdd(BackOffice->Family, &Mapping->LicenseList, &Mapping->LicenseListSize); + + ASSERT(License != NULL); + if (License != NULL) { + License->Service = BackOffice; + + // Can only claim a # of licenses that we have + if ( BackOffice->LicensesClaimed < BackOffice->Licenses) { + Claimed = BackOffice->Licenses - BackOffice->LicensesClaimed; + + if (Claimed > Mapping->Licenses) + Claimed = Mapping->Licenses; + + } + + BackOffice->LicensesUsed += Mapping->Licenses; + BackOffice->LicensesClaimed += Claimed; + License->LicensesNeeded = Mapping->Licenses - Claimed; + + Mapping->Flags |= LLS_FLAG_LICENSED; + if (License->LicensesNeeded > 0) { + License->Flags &= ~LLS_FLAG_LICENSED; + Mapping->Flags &= ~LLS_FLAG_LICENSED; + } + } + } + } + + // + // Run through all the members in the Mapping and update their license + // Compliance. + // + for (i = 0; i < Mapping->NumMembers; i++) { + pUser = UserListFind(Mapping->Members[i]); + + if ( (pUser != NULL) && (pUser->Mapping == Mapping) ) { + RtlEnterCriticalSection(&pUser->ServiceTableLock); + SvcTable = pUser->Services; + pUser->LicensedProducts = 0; + + if (Mapping->Flags & LLS_FLAG_SUITE_USE) { + if (Mapping->Flags & LLS_FLAG_LICENSED) { + pUser->Flags |= LLS_FLAG_LICENSED; + pUser->LicensedProducts = pUser->ServiceTableSize; + } else + pUser->Flags &= ~LLS_FLAG_LICENSED; + + // + // All Services and users are flagged as per BackOffice + // + for (j = 0; j < pUser->ServiceTableSize; j++) { + if (ReSynch) + SvcTable[j].License = NULL; + + if (SvcTable[j].License == NULL) { + SvcTable[j].License = License; + License->RefCount++; + } + + if (Mapping->Flags & LLS_FLAG_LICENSED) { + SvcTable[j].Flags |= LLS_FLAG_LICENSED; + } else + SvcTable[j].Flags &= ~LLS_FLAG_LICENSED; + } + } else { + BOOL Licensed = TRUE; + + // + // Fixup all the service records + // + for (j = 0; j < pUser->ServiceTableSize; j++) { + if (ReSynch) + SvcTable[j].License = NULL; + + if (SvcTable[j].License == NULL) { + SvcLicenseUpdate(pUser, &SvcTable[j]); + BackOfficeCheck = TRUE; + } + } + + // + // Now run through the services again and see if this user is + // actually licenses for all the products. + // + pUser->LicensedProducts = 0; + for (j = 0; j < pUser->ServiceTableSize; j++) + if ( (SvcTable[j].License != NULL) && (SvcTable[j].License->Flags & LLS_FLAG_LICENSED) ) { + SvcTable[j].Flags |= LLS_FLAG_LICENSED; + pUser->LicensedProducts++; + } else { + SvcTable[j].Flags &= ~LLS_FLAG_LICENSED; + Licensed = FALSE; + } + + if (Licensed) + pUser->Flags |= LLS_FLAG_LICENSED; + else + pUser->Flags &= ~LLS_FLAG_LICENSED; + } + + RtlLeaveCriticalSection(&pUser->ServiceTableLock); + } + + } + RtlReleaseResource(&MappingListLock); + + // + // Check if we need to re-check for BackOffice + // + if (BackOfficeCheck && (pUser != NULL)) + UserBackOfficeCheck( pUser ); + +} // MappingLicenseUpdate + + + +///////////////////////////////////////////////////////////////////////// +VOID +UserMappingAdd ( + PMAPPING_RECORD Mapping, + PUSER_RECORD pUser + ) + +/*++ + +Routine Description: + + Takes care of re-adjusting the licenses when we add a user to a mapping. + We need to free up any old licenses they have and point them to use + the licenses the mapping has. + +Arguments: + + +Return Value: + + +--*/ + +{ + ULONG i, j; + PUSER_LICENSE_RECORD License = NULL; + PSVC_RECORD SvcTable = NULL; + BOOL ReScan = FALSE; + +#if DBG + if (TraceFlags & TRACE_FUNCTION_TRACE) + dprintf(TEXT("LLS TRACE: UserMappingAdd\n")); +#endif + + if ( (pUser == NULL) || (Mapping == NULL) ) + return; + + // + // Run though and clean up all old licenses + // + RtlEnterCriticalSection(&pUser->ServiceTableLock); + SvcListLicenseFree(pUser); + UserLicenseListFree(pUser); + RtlLeaveCriticalSection(&pUser->ServiceTableLock); + + pUser->Mapping = Mapping; + MappingLicenseUpdate(Mapping, FALSE); + +} // UserMappingAdd + + +///////////////////////////////////////////////////////////////////////// +VOID +FamilyLicenseUpdate ( + PMASTER_SERVICE_ROOT Family + ) + +/*++ + +Routine Description: + + Used when licenses are freed-up or added to a given family of products. + Goes through the user list looking for out-of-license conditions for the + given family of products and distributes the new licenses. + +Arguments: + + +Return Value: + + +--*/ + +{ + ULONG NumLicenses = 1; + PUSER_LICENSE_RECORD License = NULL; + PMASTER_SERVICE_RECORD LicenseService = NULL; + ULONG i, j; + PUSER_RECORD pUser; + BOOL UseMapping = FALSE; + BOOL ReScan = TRUE; + +#if DBG + if (TraceFlags & TRACE_FUNCTION_TRACE) + dprintf(TEXT("LLS TRACE: FamilyLicenseUpdate\n")); +#endif + + RtlAcquireResourceShared(&UserListLock, TRUE); + + while (ReScan) { + // + // Walk user list in order of entry - adding any licenses + // + ReScan = FALSE; + i = 0; + while (i < UserListNumEntries) { + pUser = RtlGetElementGenericTable(&UserList, i); + + if (pUser != NULL) { + // + // only worry about un-licensed users + // + if ( !(pUser->Flags & LLS_FLAG_LICENSED ) ) { + // + // Find the License? + // + RtlEnterCriticalSection(&pUser->ServiceTableLock); + if (pUser->Mapping != NULL) { + License = LicenseListFind(Family->Name, pUser->Mapping->LicenseList, pUser->Mapping->LicenseListSize); + NumLicenses = pUser->Mapping->Licenses; + } else { + License = LicenseListFind(Family->Name, pUser->LicenseList, pUser->LicenseListSize); + NumLicenses = 1; + } + + // + // Make sure we need any extra licenses for this product + // + if ( (License != NULL) && (License->LicensesNeeded > 0) ) { + // + // We have found a user using this family of products in need + // of more licenses... + // + LicenseService = License->Service; + + if (pUser->Mapping != NULL) + pUser->Mapping->Flags |= LLS_FLAG_UPDATE; + + // + // Check if we can satisfy licenses using currently + // assigned service + // + if ((LicenseService->Licenses - LicenseService->LicensesClaimed) >= License->LicensesNeeded) { + LicenseService->LicensesClaimed += License->LicensesNeeded; + License->LicensesNeeded = 0; + } else { + // + // See if any other service will satisfy it... + // + while ((LicenseService != NULL) && ((LicenseService->Licenses - LicenseService->LicensesClaimed) < NumLicenses ) ) + if (LicenseService->next > 0) + LicenseService = MasterServiceTable[LicenseService->next - 1]; + else + LicenseService = NULL; + + // + // check if we found a service to satisfy licensing needs + // + if (LicenseService != NULL) { + // + // Free up any stuff - since we are freeing licenses + // we need to re-scan. + // + ReScan = TRUE; + + License->Service->LicensesUsed -= NumLicenses; + License->Service->LicensesClaimed -= (NumLicenses - License->LicensesNeeded); + + // + // Now do new stuff + // + License->Service = LicenseService; + License->Service->LicensesUsed += NumLicenses; + License->Service->LicensesClaimed += NumLicenses; + License->LicensesNeeded = 0; + } else { + // + // Eat any unclaimed licenses + // + LicenseService = License->Service; + if (LicenseService->Licenses > LicenseService->LicensesClaimed) { + License->LicensesNeeded -= (LicenseService->Licenses - LicenseService->LicensesClaimed); + LicenseService->LicensesClaimed = LicenseService->Licenses; + } + } + } + + // + // Check if we got into license + // + if (License->LicensesNeeded == 0) { + BOOL Licensed = TRUE; + + License->Flags |= LLS_FLAG_LICENSED; + + // + // this license is fulfilled so scan product list and + // adjust flags on any product using this license. + // + for (j = 0; j < pUser->ServiceTableSize; j++) { + if (pUser->Services[j].License == License) { + pUser->Services[j].Flags |= LLS_FLAG_LICENSED; + } else + if (!(pUser->Services[j].Flags & LLS_FLAG_LICENSED)) + Licensed = FALSE; + } + + // + // Recalc how many products are licensed + // + pUser->LicensedProducts = 0; + for (j = 0; j < pUser->ServiceTableSize; j++) { + if (pUser->Services[j].Flags & LLS_FLAG_LICENSED) + pUser->LicensedProducts++; + } + + if (Licensed) + pUser->Flags |= LLS_FLAG_LICENSED; + } + } + + RtlLeaveCriticalSection(&pUser->ServiceTableLock); + } + } + + i++; + } + } + + // + // If this license is for BackOffice, we have applied any licenses to + // anything set to use BackOffice. If there are still licenses left + // then see if any users should be auto-switched to BackOffice. + // + // if (Family == BackOffice->Family) { + i = 0; + while ( (BackOffice->LicensesClaimed < BackOffice->Licenses) && (i < UserListNumEntries) ) { + pUser = RtlGetElementGenericTable(&UserList, i); + + if (pUser != NULL) + UserBackOfficeCheck(pUser); + + i++; + } + // } + + RtlReleaseResource(&UserListLock); + + // + // Run through mapping and re-adjust any that need it. + // + for (i = 0; i < MappingListSize; i++) { + if (MappingList[i]->Flags & LLS_FLAG_UPDATE) { + MappingList[i]->Flags &= ~LLS_FLAG_UPDATE; + MappingLicenseUpdate( MappingList[i], FALSE ); + } + } + +} // FamilyLicenseUpdate + + +///////////////////////////////////////////////////////////////////////// +VOID +UserListLicenseDelete( + PMASTER_SERVICE_RECORD Service, + LONG Quantity +) + +/*++ + +Routine Description: + + This is used when licenses are deleted. It must walk the user-list in + the reverse order they were entered (since licenses are applied in a + FIFO manner they are removed in a LIFO manner) and delete the required + number of licenses from those consumed. + +Arguments: + + +Return Value: + + +--*/ + +{ + LONG Licenses; + ULONG i, j; + PUSER_RECORD pUser; + PSVC_RECORD pService; + ULONG NumLicenses = 1; + PUSER_LICENSE_RECORD License = NULL; + BOOL UseMapping = FALSE; + LONG Claimed; + +#if DBG + if (TraceFlags & TRACE_FUNCTION_TRACE) + dprintf(TEXT("LLS TRACE: UserListLicenseDelete\n")); +#endif + + RtlAcquireResourceShared(&UserListLock, TRUE); + + Licenses = 0 - Quantity; + + // + // Walk user list in opposite order of entry - removing licenses + // + i = UserListNumEntries - 1; + while (((LONG)i >= 0) && (Licenses > 0)) { + pUser = RtlGetElementGenericTable(&UserList, i); + + if (pUser != NULL) { + NumLicenses = 1; + UseMapping = FALSE; + + // + // If we are a mapping then we may use more then one license + // + if (pUser->Mapping != NULL) { + NumLicenses = pUser->Mapping->Licenses; + UseMapping = TRUE; + } + + // + // Try to find a license for this family of products + // + if (UseMapping) + License = LicenseListFind(Service->Family->Name, pUser->Mapping->LicenseList, pUser->Mapping->LicenseListSize); + else + License = LicenseListFind(Service->Family->Name, pUser->LicenseList, pUser->LicenseListSize); + + if (License != NULL) { + // + // Check if same as product we adjusted + // + if (License->Service == Service) { + // + // Can only release how many we took + // + Claimed = NumLicenses - License->LicensesNeeded; + if (Claimed > 0) { + if (Claimed > Licenses) { + License->LicensesNeeded += Licenses; + License->Service->LicensesClaimed -= Licenses; + Licenses = 0; + } else { + License->LicensesNeeded = NumLicenses; + License->Service->LicensesClaimed -= Claimed; + Licenses -= Claimed; + } + + License->Flags &= ~LLS_FLAG_LICENSED; + + // + // Flag any mapping that we need to recalc uses in the + // mapping + // + if (UseMapping) + pUser->Mapping->Flags |= LLS_FLAG_UPDATE; + + // + // Scan product list and adjust flags on any + // product using this license. + // + RtlEnterCriticalSection(&pUser->ServiceTableLock); + for (j = 0; j < pUser->ServiceTableSize; j++) + if (pUser->Services[j].License == License) + pUser->Services[j].Flags &= ~LLS_FLAG_LICENSED; + + // + // Recalc how many products are licensed + // + pUser->LicensedProducts = 0; + for (j = 0; j < pUser->ServiceTableSize; j++) { + if (pUser->Services[j].Flags & LLS_FLAG_LICENSED) + pUser->LicensedProducts++; + } + + RtlLeaveCriticalSection(&pUser->ServiceTableLock); + pUser->Flags &= ~LLS_FLAG_LICENSED; + } + } + } + } + + i--; + } + + RtlReleaseResource(&UserListLock); + + // + // Run through mapping and re-adjust any that need it. + // + for (i = 0; i < MappingListSize; i++) { + if (MappingList[i]->Flags & LLS_FLAG_UPDATE) { + MappingList[i]->Flags &= ~LLS_FLAG_UPDATE; + MappingLicenseUpdate( MappingList[i], FALSE ); + } + } + +} // UserListLicenseDelete + + +///////////////////////////////////////////////////////////////////////// +VOID +UserBackOfficeCheck ( + PUSER_RECORD pUser + ) + +/*++ + +Routine Description: + + Checks if the user should switch to BackOffice, and if so - does so. If + we switch to BackOffice then we need to free up any old licenes the + user may be using and claim a BackOffice License. + + Note: We will only switch to BackOffice if there are enough BackOffice + licenses available to satisfy our needs. + +Arguments: + +Return Value: + + +--*/ + +{ + DWORD Flags; + ULONG i; + ULONG LicenseListSize; + ULONG NumLicenses = 1; + PSVC_RECORD SvcTable = NULL; + PUSER_LICENSE_RECORD *LicenseList = NULL; + PUSER_LICENSE_RECORD License = NULL; + +#if DBG + if (TraceFlags & TRACE_FUNCTION_TRACE) + dprintf(TEXT("LLS TRACE: UserBackOfficeCheck\n")); +#endif + + RtlEnterCriticalSection(&pUser->ServiceTableLock); + if (pUser->Mapping != NULL) { + Flags = pUser->Mapping->Flags; + LicenseListSize = pUser->Mapping->LicenseListSize; + LicenseList = pUser->Mapping->LicenseList; + NumLicenses = pUser->Mapping->Licenses; + } else { + Flags = pUser->Flags; + LicenseListSize = pUser->LicenseListSize; + LicenseList = pUser->LicenseList; + } + + // + // If we are already using BackOffice - get out + // + if (Flags & LLS_FLAG_SUITE_USE) { + RtlLeaveCriticalSection(&pUser->ServiceTableLock); + return; + } + + if ( Flags & LLS_FLAG_SUITE_AUTO ) + // + // if we aren't licensed, or the # services == auto switch threshold + // then switch to using BackOffice + // + if ((!(Flags & LLS_FLAG_LICENSED)) || ((LicenseListSize + 1) >= BACKOFFICE_SWITCH) ) { + // + // Make sure we have licenses for this + // + RtlAcquireResourceExclusive(&MasterServiceListLock, TRUE); + if ( BackOffice->Licenses >= (NumLicenses + BackOffice->LicensesClaimed) ) { + // + // Free up the old licenses - temporarily claim the BackOffice + // licenses so somebody else won't. + // + BackOffice->LicensesClaimed += NumLicenses; + UserLicenseListFree(pUser); + BackOffice->LicensesClaimed -= NumLicenses; + + // + // UserLicenseListFree might have assigned us a license in + // the rescan if we are part of a mapping so check this. + // + if (pUser->Mapping != NULL) + Flags = pUser->Mapping->Flags; + else + Flags = pUser->Flags; + + // + // If we are already using BackOffice - get out + // + if (Flags & LLS_FLAG_SUITE_USE) { + RtlLeaveCriticalSection(&pUser->ServiceTableLock); + RtlReleaseResource(&MasterServiceListLock); + return; + } + + // + // And if part of a mapping free those up + // + if (pUser->Mapping != NULL) + MappingLicenseListFree(pUser->Mapping); + + // + // Now add the BackOffice License + // + if (pUser->Mapping != NULL) { + pUser->Mapping->LicenseList = NULL; + pUser->Mapping->LicenseListSize = 0; + + License = LicenseListAdd(BackOffice->Family, &pUser->Mapping->LicenseList, &pUser->Mapping->LicenseListSize); + + LicenseList = pUser->Mapping->LicenseList; + LicenseListSize = pUser->Mapping->LicenseListSize; + } else { + pUser->LicenseList = NULL; + pUser->LicenseListSize = 0; + + License = LicenseListAdd(BackOffice->Family, &pUser->LicenseList, &pUser->LicenseListSize); + + LicenseList = pUser->LicenseList; + LicenseListSize = pUser->LicenseListSize; + } + + ASSERT(License != NULL); + if (License != NULL) + License->Service = BackOffice; + + // + // if mapping adjust mapping records then go through all users and + // adjust them + // + if (pUser->Mapping != NULL) { + pUser->Mapping->Flags |= LLS_FLAG_SUITE_USE; + pUser->Mapping->Flags |= LLS_FLAG_LICENSED; + + BackOffice->LicensesUsed += NumLicenses; + BackOffice->LicensesClaimed += NumLicenses; + + RtlLeaveCriticalSection(&pUser->ServiceTableLock); + RtlReleaseResource(&MasterServiceListLock); + + MappingLicenseUpdate(pUser->Mapping, TRUE); + return; + } else { + pUser->Flags |= LLS_FLAG_SUITE_USE; + pUser->Flags |= LLS_FLAG_LICENSED; + + pUser->LicensedProducts = pUser->ServiceTableSize; + BackOffice->LicensesUsed += NumLicenses; + BackOffice->LicensesClaimed += NumLicenses; + + // + // Run through products & licenses adjusting licenses + // + SvcTable = pUser->Services; + for (i = 0; i < pUser->ServiceTableSize; i++) { + SvcTable[i].Flags |= LLS_FLAG_LICENSED; + SvcTable[i].License = License; + SvcTable[i].License->RefCount++; + } + + } + + } + + RtlReleaseResource(&MasterServiceListLock); + + } + + RtlLeaveCriticalSection(&pUser->ServiceTableLock); + +} // UserBackOfficeCheck + + +///////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////// +// +// Utility routines for managing the user and SID lists - used mostly +// by the splay table routines. + +///////////////////////////////////////////////////////////////////////// +RTL_GENERIC_COMPARE_RESULTS +SidListCompare ( + struct _RTL_GENERIC_TABLE *Table, + PVOID FirstStruct, + PVOID SecondStruct + ) + +/*++ + +Routine Description: + + +Arguments: + + Table - + + FirstStruct - + + SecondStruct - + +Return Value: + + +--*/ + +{ + PUSER_RECORD UseRec1, UseRec2; + int ret; + + if ((FirstStruct == NULL) || (SecondStruct == NULL)) + return GenericEqual; + + UseRec1 = (PUSER_RECORD) FirstStruct; + UseRec2 = (PUSER_RECORD) SecondStruct; + + if (UseRec1->IDSize == UseRec2->IDSize) { + ret = memcmp((PVOID) UseRec1->UserID, (PVOID) UseRec2->UserID, UseRec1->IDSize); + if (ret < 0) + return GenericLessThan; + else if (ret > 0) + return GenericGreaterThan; + else + return GenericEqual; + } else + // + // Not same size, so just compare length + // + if (UseRec1->IDSize > UseRec2->IDSize) + return GenericGreaterThan; + else + return GenericLessThan; + +} // SidListCompare + + +///////////////////////////////////////////////////////////////////////// +RTL_GENERIC_COMPARE_RESULTS +UserListCompare ( + struct _RTL_GENERIC_TABLE *Table, + PVOID FirstStruct, + PVOID SecondStruct + ) + +/*++ + +Routine Description: + + +Arguments: + + Table - + + FirstStruct - + + SecondStruct - + +Return Value: + + +--*/ + +{ + PUSER_RECORD UseRec1, UseRec2; + int ret; + + if ((FirstStruct == NULL) || (SecondStruct == NULL)) + return GenericEqual; + + UseRec1 = (PUSER_RECORD) FirstStruct; + UseRec2 = (PUSER_RECORD) SecondStruct; + + ret = lstrcmpi((LPTSTR) UseRec1->UserID, (LPTSTR) UseRec2->UserID); + + if (ret < 0) + return GenericLessThan; + else if (ret > 0) + return GenericGreaterThan; + else + return GenericEqual; + +} // UserListCompare + + +///////////////////////////////////////////////////////////////////////// +PVOID +UserListAlloc ( + struct _RTL_GENERIC_TABLE *Table, + CLONG ByteSize + ) + +/*++ + +Routine Description: + + +Arguments: + + Table - + + ByteSize - + +Return Value: + + +--*/ + +{ + return (PVOID) LocalAlloc(LPTR, ByteSize); + +} // UserListAlloc + + +///////////////////////////////////////////////////////////////////////// +VOID +UserListFree ( + struct _RTL_GENERIC_TABLE *Table, + PVOID Buffer + ) + +/*++ + +Routine Description: + + +Arguments: + + Table - + + Buffer - + +Return Value: + + +--*/ + +{ + PUSER_RECORD UserRec; + + if (Buffer == NULL) + return; + + UserRec = (PUSER_RECORD) Buffer; + LocalFree(UserRec->UserID); + LocalFree(UserRec); + +} // UserListFree + + +///////////////////////////////////////////////////////////////////////// +PUSER_RECORD +UserListFind( + LPTSTR UserName +) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ + +{ + USER_RECORD UserRec; + PUSER_RECORD pUserRec; + +#if DBG + if (TraceFlags & TRACE_FUNCTION_TRACE) + dprintf(TEXT("LLS TRACE: UserListFind\n")); +#endif + + UserRec.UserID = (PVOID) UserName; + + RtlEnterCriticalSection(&GenTableLock); + pUserRec = (PUSER_RECORD) RtlLookupElementGenericTable(&UserList, &UserRec); + RtlLeaveCriticalSection(&GenTableLock); + + return pUserRec; + +} // UserListFind + + + +///////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////// + +///////////////////////////////////////////////////////////////////////// +VOID +UserListAdd( + PMASTER_SERVICE_RECORD Service, + ULONG DataType, + ULONG DataLength, + PVOID Data, + ULONG AccessCount, + DWORD LastAccess, + DWORD FlagsParam +) + +/*++ + +Routine Description: + + Routine called by the Add cache routine to update the user and/or SID + lists with the new service information. + +Arguments: + + +Return Value: + + +--*/ + +{ + USER_RECORD UserRec; + PUSER_RECORD pUserRec; + BOOLEAN Added; + PSVC_RECORD pService; + PSVC_RECORD SvcTable = NULL; + PRTL_GENERIC_TABLE pTable = NULL; + PRTL_RESOURCE pLock = NULL; + PRTL_RESOURCE pAddEnumLock = NULL; + BOOL SIDSwitch = FALSE; + BOOL UserLock = FALSE; + BOOL UserEnumLock = FALSE; + PMAPPING_RECORD pMap = NULL; + ULONG ProductLicenses, ProductLicensesUsed, i; + BOOL SwitchToBackOffice = FALSE; + +#if DBG + if (TraceFlags & TRACE_FUNCTION_TRACE) + dprintf(TEXT("LLS TRACE: UserListAdd\n")); +#endif + + // only 2 bits are used + ASSERT( FlagsParam == ( FlagsParam & ( LLS_FLAG_SUITE_USE | LLS_FLAG_SUITE_AUTO ) ) ); + + // + // Set up lock and table pointers based on if data is SID or username... + // + UserRec.UserID = Data; + if (DataType == DATA_TYPE_USERNAME) { + pTable = &UserList; + pLock = &UserListLock; + pAddEnumLock = &UserListAddEnumLock; + } else if (DataType == DATA_TYPE_SID) { + pTable = &SidList; + pLock = &SidListLock; + pAddEnumLock = &SidListAddEnumLock; + } + + if (pTable == NULL) + return; + + // + // The generic table package copies the record so the record is + // temporary, but since we store the string as a pointer the pointer is + // copied but the actual memory that is pointed to is kept around + // permenantly. + // + // We have already allocated memory for the Data + // + UserRec.UserID = Data; + UserRec.IDSize = DataLength; + + UserRec.Flags = FlagsParam; + UserRec.LicensedProducts = 0; + UserRec.LastReplicated = 0; + UserRec.ServiceTableSize = 0; + UserRec.Services = NULL; + UserRec.Mapping = NULL; + UserRec.LicenseListSize = 0; + UserRec.LicenseList = NULL; + + // + // Assume that the user is licensed - we will blast it if they aren't + // down below. + // + UserRec.Flags |= LLS_FLAG_LICENSED; + + // + // Need to update list so get exclusive access. First get Add/Enum lock + // so we don't block reads if doing an enum. + // + RtlAcquireResourceExclusive(pAddEnumLock, TRUE); + RtlAcquireResourceExclusive(pLock, TRUE); + + pUserRec = (PUSER_RECORD) RtlInsertElementGenericTable(pTable, (PVOID) &UserRec, sizeof(USER_RECORD), &Added); + + if (pUserRec == NULL) { + ASSERT(FALSE); + LocalFree(UserRec.UserID); + RtlReleaseResource(pLock); + RtlReleaseResource(pAddEnumLock); + return; + } + + pUserRec->Flags &= ~LLS_FLAG_DELETED; + + // if auto suite is ever turned off, it's gone for good + if ( ! ( FlagsParam & LLS_FLAG_SUITE_AUTO ) ) + { + // set suite use to be that specified in the function parameters + pUserRec->Flags &= ~LLS_FLAG_SUITE_AUTO; + pUserRec->Flags |= ( FlagsParam & LLS_FLAG_SUITE_USE ); + } + + // + // If for some reason the record is already there then we need to + // clean-up the name we allocated. + // + if (Added == FALSE) { + LocalFree(UserRec.UserID); + + // + // If this is a SID then check the SID record to find the corresponding + // USER_RECORD (it better be there) and update that instead.. Note: We + // kludge this by storing the pointer to the user table in the + // LastReplicated field. + // + if ((DataType == DATA_TYPE_SID) && (pUserRec->LastReplicated != 0)) { + // + // Switch data as approp. + // + SIDSwitch = TRUE; + } + } else { + // + // Do this here so when we release to READ access another thread + // won't AV when trying to get access to it. + // + RtlInitializeCriticalSection(&pUserRec->ServiceTableLock); + + if (DataType == DATA_TYPE_USERNAME) { + pMap = MappingListUserFind(UserRec.UserID); + pUserRec->Mapping = pMap; + UserListNumEntries++; + } else + SidListNumEntries++; + + } + + // + // If this is a SID, and we haven't gotten an appropriate user-rec + // then try to de-reference this and get the appropriate user-rec. + // + if ((DataType == DATA_TYPE_SID) && (pUserRec->LastReplicated == 0)) { + TCHAR UserName[MAX_USERNAME_LENGTH + 1]; + TCHAR DomainName[MAX_DOMAINNAME_LENGTH + 1]; + TCHAR FullName[MAX_USERNAME_LENGTH + MAX_DOMAINNAME_LENGTH + 2]; + SID_NAME_USE snu; + PUSER_RECORD pUserRec2; + DWORD unSize, dnSize; + + unSize = sizeof(UserName); + dnSize = sizeof(DomainName); + if (LookupAccountSid(NULL, (PSID) Data, UserName, &unSize, DomainName, &dnSize, &snu)) { + // + // Okay, de-referenced the SID, so go get the user-rec, but pre-pend + // domain first... + // + lstrcpy(FullName, DomainName); + lstrcat(FullName, TEXT("\\")); + lstrcat(FullName, UserName); + UserRec.UserID = FullName; + UserRec.IDSize = (lstrlen(FullName) + 1) * sizeof(TCHAR); + + // + // Get locks, we will try shared first. + // + RtlAcquireResourceShared(&UserListLock, TRUE); + UserLock = TRUE; + SIDSwitch = TRUE; + + RtlEnterCriticalSection(&GenTableLock); + pUserRec2 = (PUSER_RECORD) RtlLookupElementGenericTable(&UserList, &UserRec); + RtlLeaveCriticalSection(&GenTableLock); + if (pUserRec2 != NULL) { + // + // Tarnation! we found it - so use it. + // + pUserRec->LastReplicated = (ULONG) pUserRec2; + } else { + // + // Dang it all... It ain't in the dern table, so we're gonna + // put it there. First need to alloc perm storage for UserID + // + UserRec.UserID = LocalAlloc(LPTR, UserRec.IDSize); + if (UserRec.UserID != NULL) { + lstrcpy((LPTSTR) UserRec.UserID, FullName); + + // + // Need to update list so get exclusive access. First get + // Add/Enum lock so we don't block reads if doing an enum. + // + RtlAcquireResourceExclusive(&UserListAddEnumLock, TRUE); + UserEnumLock = TRUE; + RtlConvertSharedToExclusive(&UserListLock); + pUserRec2 = (PUSER_RECORD) RtlInsertElementGenericTable(&UserList, (PVOID) &UserRec, sizeof(USER_RECORD), &Added); + } + + // + // If we couldn't insert it then seomthing is wrong, clean up + // and exit. + // + if (pUserRec2 == NULL) { + ASSERT(FALSE); + + if (UserRec.UserID != NULL) + LocalFree(UserRec.UserID); + + RtlReleaseResource(pLock); + RtlReleaseResource(pAddEnumLock); + + RtlReleaseResource(&UserListLock); + RtlReleaseResource(&UserListAddEnumLock); + return; + } + + // + // Update SID USER_REC pointer (LastReplicated) and then finally + // free up the SID lock + // + pUserRec->LastReplicated = (ULONG) pUserRec2; + + if (Added == TRUE) { + // + // Do this here so when we release to READ access another + // thread won't AV when trying to get access to it. + // + RtlInitializeCriticalSection(&pUserRec2->ServiceTableLock); + pMap = MappingListUserFind(UserRec.UserID); + pUserRec2->Mapping = pMap; + UserListNumEntries++; + } + + RtlConvertExclusiveToShared(&UserListLock); + + } + + // + // We have found or added a USER_REC for the SID which pUserRec2 + // points to. Now we need to switch locks and tables. + // + } + } + + // + // If we need to munge from SID to User tables, then do so... + // + if (SIDSwitch) { + // + // Switch data as approp. + // + pUserRec = (PUSER_RECORD) pUserRec->LastReplicated; + DataType = DATA_TYPE_USERNAME; + + // + // Release locks on SID Table + // + RtlReleaseResource(pLock); + RtlReleaseResource(pAddEnumLock); + + // + // Now switch locks to User Table + // + pTable = &UserList; + pLock = &UserListLock; + pAddEnumLock = &UserListAddEnumLock; + + if (UserEnumLock) + RtlReleaseResource(pAddEnumLock); + + if (!UserLock) + RtlAcquireResourceShared(pLock, TRUE); + } else { + // + // No longer need exclusive access + // + RtlConvertExclusiveToShared(pLock); + + // + // Also - no longer need Add/Enum Lock + // + RtlReleaseResource(pAddEnumLock); + } + + // + // At this point we have either found the old record, or added a new + // one. In either case pUserRec points to the correct record. + // + if (pUserRec != NULL) { + // + // Check Service table to make sure our service is already there + // + RtlEnterCriticalSection(&pUserRec->ServiceTableLock); + pService = SvcListFind( Service->Name, pUserRec->Services, pUserRec->ServiceTableSize ); + + if (pService != NULL) { + // + // Found entry in service table so just increment count + // + if (pService->AccessCount + AccessCount < MAX_ACCESS_COUNT) + pService->AccessCount += AccessCount; + else + pService->AccessCount = MAX_ACCESS_COUNT; + + pService->LastAccess = LastAccess; + } else { + // + // Need to add more entries to service table (or create it...) + // + if (pUserRec->Services == NULL) + SvcTable = (PSVC_RECORD) LocalAlloc( LPTR, sizeof(SVC_RECORD)); + else + SvcTable = (PSVC_RECORD) LocalReAlloc( pUserRec->Services, sizeof(SVC_RECORD) * (pUserRec->ServiceTableSize + 1), LHND); + + pUserRec->Services = SvcTable; + ASSERT(SvcTable != NULL); + if (SvcTable != NULL) { + DWORD Flags; + + if (pUserRec->Mapping != NULL) + Flags = pUserRec->Mapping->Flags; + else + Flags = pUserRec->Flags; + + SvcTable[pUserRec->ServiceTableSize].Service = Service; + SvcTable[pUserRec->ServiceTableSize].LastAccess = LastAccess; + + // + // Update AccessCount field, but make sure we don't roll over + // + if (AccessCount < MAX_ACCESS_COUNT) + SvcTable[pUserRec->ServiceTableSize].AccessCount = AccessCount; + else + SvcTable[pUserRec->ServiceTableSize].AccessCount = MAX_ACCESS_COUNT; + + SvcTable[pUserRec->ServiceTableSize].Flags = LLS_FLAG_LICENSED; + + // + // Now update the actual license info + // + SvcLicenseUpdate(pUserRec, &SvcTable[pUserRec->ServiceTableSize]); + pUserRec->ServiceTableSize += 1; + + if (SvcTable[pUserRec->ServiceTableSize - 1].Flags & LLS_FLAG_LICENSED) + pUserRec->LicensedProducts++; + + // + // If the added product isn't licensed then update user flag + // + if (IsMaster && !(SvcTable[pUserRec->ServiceTableSize - 1].Flags & LLS_FLAG_LICENSED) ) + pUserRec->Flags &= ~LLS_FLAG_LICENSED; + + // Now that it is all setup - sort the table (so search will work) + qsort((void *) pUserRec->Services, (size_t) pUserRec->ServiceTableSize, sizeof(SVC_RECORD), SvcListCompare); + + UserBackOfficeCheck ( pUserRec ); + } + + } + RtlLeaveCriticalSection(&pUserRec->ServiceTableLock); + + } + + RtlReleaseResource(pLock); +} // UserListAdd + + +///////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////// +// +// The AddCache routines are a queue of User Identifiers (Username or +// SID's) and the service being accessed. Records are dequeued by a +// background thread and handed off to the UserListAdd function. + +///////////////////////////////////////////////////////////////////////// +VOID +AddCacheManager ( + IN PVOID ThreadParameter + ) + +/*++ + +Routine Description: + + +Arguments: + + ThreadParameter - Not used. + + +Return Value: + + This thread never exits. + +--*/ + +{ + NTSTATUS Status; + PADD_CACHE pAdd; + + // + // Loop forever waiting to be given the opportunity to serve the + // greater good. + // + for ( ; ; ) { + // + // Wait to be notified that there is work to be done + // + Status = NtWaitForSingleObject( LLSAddCacheEvent, TRUE, NULL ); + + // + // Take an item from the add cache. + // + RtlEnterCriticalSection(&AddCacheLock); + while (AddCache != NULL) { + pAdd = AddCache; + AddCache = AddCache->prev; + AddCacheSize--; + + RtlLeaveCriticalSection(&AddCacheLock); + + if (pAdd != NULL) { + UserListAdd(pAdd->Service, pAdd->DataType, pAdd->DataLength, pAdd->Data, pAdd->AccessCount, pAdd->LastAccess, pAdd->Flags); + LocalFree(pAdd); + } + + Sleep(0); + // + // Need to re-enter critical section to check in the while loop + RtlEnterCriticalSection(&AddCacheLock); + } + + RtlLeaveCriticalSection(&AddCacheLock); + } + +} // AddCacheManager + + +///////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////// + +///////////////////////////////////////////////////////////////////////// +VOID +UserListInit() + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ + +{ + NTSTATUS Status; + DWORD Ignore; + HANDLE Thread; + + // + // Initialize the generic table. + // + RtlInitializeGenericTable ( &UserList, + UserListCompare, + UserListAlloc, + UserListFree, + (PVOID) TEXT("LLS Table") ); + + RtlInitializeCriticalSection(&GenTableLock); + RtlInitializeResource(&UserListLock); + RtlInitializeResource(&UserListAddEnumLock); + + // + // Initialize the SID table. + // + RtlInitializeGenericTable ( &SidList, + SidListCompare, + UserListAlloc, + UserListFree, + (PVOID) TEXT("LLS SID Table") ); + + RtlInitializeResource(&SidListLock); + RtlInitializeResource(&SidListAddEnumLock); + + // + // Now our add cache + // + RtlInitializeCriticalSection(&AddCacheLock); + + // + // Create the Add Cache Management event + // + Status = NtCreateEvent( + &LLSAddCacheEvent, + EVENT_QUERY_STATE | EVENT_MODIFY_STATE | SYNCHRONIZE, + NULL, + SynchronizationEvent, + FALSE + ); + + ASSERT(NT_SUCCESS(Status)); + + // + // Create the Add Cache management thread + // + Thread = CreateThread( + NULL, + 0L, + (LPTHREAD_START_ROUTINE) AddCacheManager, + 0L, + 0L, + &Ignore + ); + + + LastUsedTime = DateSystemGet(); + +} // UserListInit + + +///////////////////////////////////////////////////////////////////////// +VOID +UserListUpdate( + ULONG DataType, + PVOID Data, + PSERVICE_RECORD Service +) + +/*++ + +Routine Description: + + Actual start of the perseat license code. Given a SID or UserName find + the record in the appropriate table and check the given service. If the + service is already there then the info is updated, if it isn't there then + the record is put onto the add cache queue for background processing. + +Arguments: + + +Return Value: + + +--*/ + +{ + USER_RECORD UserRec; + PUSER_RECORD pUserRec; + BOOLEAN Added; + ULONG DataLength; + PSVC_RECORD pService; + PSVC_RECORD SvcTable = NULL; + PRTL_GENERIC_TABLE pTable = NULL; + PRTL_RESOURCE pLock = NULL; + PADD_CACHE pAdd = NULL; + NTSTATUS NtStatus; + BOOL ToAddCache = FALSE; + BOOL FullName = TRUE; + LPTSTR pName; + +#if DBG + if (TraceFlags & TRACE_FUNCTION_TRACE) + dprintf(TEXT("LLS TRACE: UserListUpdate\n")); +#endif + + // + // Setup table and lock pointers based if Data is UserName or SID + // + UserRec.UserID = Data; + if (DataType == DATA_TYPE_USERNAME) { + pTable = &UserList; + pLock = &UserListLock; + DataLength = (lstrlen((LPWSTR) Data) + 1) * sizeof(TCHAR); + } else if (DataType == DATA_TYPE_SID) { + pTable = &SidList; + pLock = &SidListLock; + DataLength = RtlLengthSid((PSID) Data); + } + + if (pTable == NULL) + return; + + // + // Searching so don't need exclusive access + // + RtlAcquireResourceShared(pLock, TRUE); + + RtlEnterCriticalSection(&GenTableLock); + pUserRec = (PUSER_RECORD) RtlLookupElementGenericTable(pTable, &UserRec); + RtlLeaveCriticalSection(&GenTableLock); + if (pUserRec == NULL) + ToAddCache = TRUE; + else { + // + // pUserRec now points to the record we must update. + // + // Check Service table to make sure our service is already there + // + pUserRec->Flags &= ~LLS_FLAG_DELETED; + RtlEnterCriticalSection(&pUserRec->ServiceTableLock); + pService = SvcListFind( Service->DisplayName, pUserRec->Services, pUserRec->ServiceTableSize ); + + if (pService == NULL) + ToAddCache = TRUE; + else { + // + // Found entry in service table so just increment count + // + pService->AccessCount += 1; + pService->LastAccess = LastUsedTime; + } + RtlLeaveCriticalSection(&pUserRec->ServiceTableLock); + + } + + RtlReleaseResource(pLock); + + if (ToAddCache) { + // + // Couldn't find the specific user/service, so put it on the Add Cache. + // First alloc memory for the name and Add Cache record. + // + pAdd = LocalAlloc(LPTR, sizeof(ADD_CACHE)); + if (pAdd == NULL) { + ASSERT(FALSE); + return; + } + + if (DataType == DATA_TYPE_USERNAME) { + FullName = FALSE; + pName = (LPTSTR) Data; + + // + // Make sure first char isn't backslash, if not then look for + // backslash as domain-name. If first char is backslash then get + // rid of it. + // + if (*pName != TEXT('\\')) + while ((*pName != TEXT('\0')) && !FullName) { + if (*pName == TEXT('\\')) + FullName = TRUE; + + pName++; + } + else + ((LPTSTR) Data)++; + + } + + // + // If we don't have a fully qualified Domain\Username, then tack the + // Domain name onto the name. + // + if (!FullName) { + UserRec.UserID = LocalAlloc( LPTR, DataLength + MyDomainSize); + + if (UserRec.UserID == NULL) { + ASSERT(FALSE); + LocalFree(pAdd); + return; + } + + pAdd->Data = UserRec.UserID; + + lstrcpy((LPTSTR) pAdd->Data, MyDomain); + lstrcat((LPTSTR) pAdd->Data, (LPTSTR) Data); + pAdd->DataLength = DataLength + MyDomainSize; + + } else { + UserRec.UserID = LocalAlloc( LPTR, DataLength); + + if (UserRec.UserID == NULL) { + ASSERT(FALSE); + LocalFree(pAdd); + return; + } + + pAdd->Data = UserRec.UserID; + memcpy(pAdd->Data, Data, DataLength); + pAdd->DataLength = DataLength; + } + + // + // copy over all the data fields into the newly created Add Cache + // record. + // + pAdd->DataType = DataType; + pAdd->Service = Service->MasterService; + pAdd->AccessCount = 1; + pAdd->LastAccess = LastUsedTime; + pAdd->Flags = LLS_FLAG_SUITE_AUTO; + + // + // Now update the actual Add Cache + // + RtlEnterCriticalSection(&AddCacheLock); + pAdd->prev = AddCache; + AddCache = pAdd; + AddCacheSize++; + RtlLeaveCriticalSection(&AddCacheLock); + + // + // Now must signal the event so we can pull off the new record. + // + NtStatus = NtSetEvent( LLSAddCacheEvent, NULL ); + ASSERT(NT_SUCCESS(NtStatus)); + } + +} // UserListUpdate + + +#if DBG +///////////////////////////////////////////////////////////////////////// +VOID +AddCacheDebugDump ( ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ + +{ + NTSTATUS Status; + PADD_CACHE pAdd; + UNICODE_STRING UString; + ULONG i = 0; + + RtlEnterCriticalSection(&AddCacheLock); + + dprintf(TEXT("Add Cache Dump. Record Size: %4lu # Entries: %lu\n"), sizeof(ADD_CACHE), AddCacheSize); + pAdd = AddCache; + + while (pAdd != NULL) { + if (pAdd->DataType == DATA_TYPE_USERNAME) + dprintf(TEXT("%4lu) Svc: %s User: [%2lu] %s\n"), + ++i, + pAdd->Service, + pAdd->DataLength, + pAdd->Data); + else if (pAdd->DataType == DATA_TYPE_SID) { + Status = RtlConvertSidToUnicodeString(&UString, (PSID) pAdd->Data, TRUE); + + dprintf(TEXT("%4lu) Svc: %s User: [%2lu] %s\n"), + ++i, + pAdd->Service, + pAdd->DataLength, + UString.Buffer); + + RtlFreeUnicodeString(&UString); + } + + pAdd = pAdd->prev; + } + + RtlLeaveCriticalSection(&AddCacheLock); + +} // AddCacheDebugDump + + +///////////////////////////////////////////////////////////////////////// +VOID +UserListDebugDump( ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ + +{ + ULONG i = 0; + PUSER_RECORD UserRec = NULL; + PVOID RestartKey = NULL; + + RtlAcquireResourceShared(&UserListLock, TRUE); + + dprintf(TEXT("User List Dump. Record Size: %4lu # Entries: %lu\n"), sizeof(USER_RECORD), UserListNumEntries); + UserRec = (PUSER_RECORD) RtlEnumerateGenericTableWithoutSplaying(&UserList, (VOID **) &RestartKey); + + while (UserRec != NULL) { + // + // Dump info for found user-rec + // + if (UserRec->Mapping != NULL) + dprintf(TEXT("%4lu) Repl: %s LT: %2lu Svc: %2lu Flags: 0x%4lX Map: %s User: [%2lu] %s\n"), + ++i, + TimeToString(UserRec->LastReplicated), + UserRec->LicenseListSize, + UserRec->ServiceTableSize, + UserRec->Flags, + UserRec->Mapping->Name, + UserRec->IDSize, + (LPTSTR) UserRec->UserID ); + else + dprintf(TEXT("%4lu) Repl: %s LT: %2lu Svc: %2lu Flags: 0x%4lX User: [%2lu] %s\n"), + ++i, + TimeToString(UserRec->LastReplicated), + UserRec->LicenseListSize, + UserRec->ServiceTableSize, + UserRec->Flags, + UserRec->IDSize, + (LPTSTR) UserRec->UserID ); + + // Get next record + UserRec = (PUSER_RECORD) RtlEnumerateGenericTableWithoutSplaying(&UserList, (VOID **) &RestartKey); + } + + RtlReleaseResource(&UserListLock); +} // UserListDebugDump + + +///////////////////////////////////////////////////////////////////////// +VOID +UserListDebugInfoDump( + PVOID Data + ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ + +{ + USER_RECORD UserRec; + PUSER_RECORD pUserRec; + PSVC_RECORD SvcTable = NULL; + ULONG i; + + RtlAcquireResourceShared(&UserListLock, TRUE); + dprintf(TEXT("User List Info. Record Size: %4lu # Entries: %lu\n"), sizeof(USER_RECORD), UserListNumEntries); + + // + // Only dump user if one was specified. + // + if (lstrlen((LPWSTR) Data) > 0) { + UserRec.UserID = Data; + + RtlEnterCriticalSection(&GenTableLock); + pUserRec = (PUSER_RECORD) RtlLookupElementGenericTable(&UserList, &UserRec); + RtlLeaveCriticalSection(&GenTableLock); + + if (pUserRec != NULL) { + // + // Dump info for found user-rec + // + if (pUserRec->Mapping != NULL) + dprintf(TEXT(" Repl: %s LT: %2lu Svc: %2lu Flags: 0x%4lX Map: %s User: [%2lu] %s\n"), + TimeToString(pUserRec->LastReplicated), + pUserRec->LicenseListSize, + pUserRec->ServiceTableSize, + pUserRec->Flags, + pUserRec->Mapping->Name, + pUserRec->IDSize, + (LPTSTR) pUserRec->UserID ); + else + dprintf(TEXT(" Repl: %s LT: %2lu Svc: %2lu Flags: 0x%4lX User: [%2lu] %s\n"), + TimeToString(pUserRec->LastReplicated), + pUserRec->LicenseListSize, + pUserRec->ServiceTableSize, + pUserRec->Flags, + pUserRec->IDSize, + (LPTSTR) pUserRec->UserID ); + + // + // Now do the service table - but get critical section first. + // + RtlEnterCriticalSection(&pUserRec->ServiceTableLock); + SvcTable = pUserRec->Services; + + if (pUserRec->ServiceTableSize != 0) + dprintf(TEXT("\nServiceTable\n")); + + for (i = 0; i < pUserRec->ServiceTableSize; i++) + dprintf( TEXT(" AC: %4lu LA: %s Flags: 0x%4lX Svc: %s\n"), + SvcTable[i].AccessCount, + TimeToString(SvcTable[i].LastAccess), + SvcTable[i].Flags, + SvcTable[i].Service->Name ); + + if (pUserRec->LicenseListSize != 0) + dprintf(TEXT("\nLicenseTable\n")); + + for (i = 0; i < pUserRec->LicenseListSize; i++) + dprintf( TEXT(" Flags: 0x%4lX Ref: %2lu LN: %2lu Svc: %s\n"), + pUserRec->LicenseList[i]->Flags, + pUserRec->LicenseList[i]->RefCount, + pUserRec->LicenseList[i]->LicensesNeeded, + pUserRec->LicenseList[i]->Service->Name ); + + RtlLeaveCriticalSection(&pUserRec->ServiceTableLock); + + } else + dprintf(TEXT("User Not Found: %s\n"), (LPWSTR) Data); + } + + RtlReleaseResource(&UserListLock); + +} // UserListDebugInfoDump + + +///////////////////////////////////////////////////////////////////////// +VOID +UserListDebugFlush( ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ + +{ + USER_RECORD UserRec; + + // + // Searching so don't need exclusive access + // + RtlAcquireResourceExclusive(&UserListLock, TRUE); + + RtlEnterCriticalSection(&GenTableLock); + RtlLookupElementGenericTable(&UserList, &UserRec); + RtlLeaveCriticalSection(&GenTableLock); + + RtlReleaseResource(&UserListLock); +} // UserListDebugFlush + + +///////////////////////////////////////////////////////////////////////// +VOID +SidListDebugDump( ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ + +{ + ULONG i = 0; + PUSER_RECORD UserRec = NULL; + UNICODE_STRING UString; + NTSTATUS NtStatus; + PVOID RestartKey = NULL; + + RtlAcquireResourceShared(&SidListLock, TRUE); + + dprintf(TEXT("SID List Dump. Record Size: %4lu # Entries: %lu\n"), sizeof(USER_RECORD), SidListNumEntries); + UserRec = (PUSER_RECORD) RtlEnumerateGenericTableWithoutSplaying(&SidList, (VOID **) &RestartKey); + + while (UserRec != NULL) { + // + // Dump info for found user-rec + // + NtStatus = RtlConvertSidToUnicodeString(&UString, (PSID) UserRec->UserID, TRUE); + dprintf(TEXT("%4lu) User-Rec: 0x%lX Svc: %2lu User: [%2lu] %s\n"), + ++i, + UserRec->LastReplicated, + UserRec->ServiceTableSize, + UserRec->IDSize, + UString.Buffer ); + + RtlFreeUnicodeString(&UString); + + // Get next record + UserRec = (PUSER_RECORD) RtlEnumerateGenericTableWithoutSplaying(&SidList, (VOID **) &RestartKey); + } + + RtlReleaseResource(&SidListLock); +} // SidListDebugDump + + +///////////////////////////////////////////////////////////////////////// +VOID +SidListDebugInfoDump( + PVOID Data + ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ + +{ + USER_RECORD UserRec; + PUSER_RECORD pUserRec; + PSVC_RECORD SvcTable = NULL; + ULONG i; + + RtlAcquireResourceShared(&SidListLock, TRUE); + dprintf(TEXT("SID List Info. Record Size: %4lu # Entries: %lu\n"), sizeof(USER_RECORD), SidListNumEntries); + + // + // Only dump user if one was specified. + // + if (lstrlen((LPWSTR) Data) > 0) { + UserRec.UserID = Data; + + RtlEnterCriticalSection(&GenTableLock); + pUserRec = (PUSER_RECORD) RtlLookupElementGenericTable(&SidList, &UserRec); + RtlLeaveCriticalSection(&GenTableLock); + + if (pUserRec != NULL) { + // + // Dump info for found user-rec + // + dprintf(TEXT(" User-Rec: 0x%lX Svc: %2lu User: [%2lu] %s\n"), + pUserRec->LastReplicated, + pUserRec->ServiceTableSize, + pUserRec->IDSize, + (LPTSTR) pUserRec->UserID ); + + // No Service Table for SID's + } else + dprintf(TEXT("SID Not Found: %s\n"), (LPWSTR) Data); + } + + RtlReleaseResource(&SidListLock); + +} // SidListDebugInfoDump + + +///////////////////////////////////////////////////////////////////////// +VOID +SidListDebugFlush( ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ + +{ + USER_RECORD UserRec; + + // + // Searching so don't need exclusive access + // + RtlAcquireResourceExclusive(&SidListLock, TRUE); + + RtlReleaseResource(&SidListLock); +} // SidListDebugFlush + + +#endif diff --git a/private/net/svcdlls/lls/server/perseat.h b/private/net/svcdlls/lls/server/perseat.h new file mode 100644 index 000000000..00257f1c6 --- /dev/null +++ b/private/net/svcdlls/lls/server/perseat.h @@ -0,0 +1,235 @@ +/*++ + +Copyright (c) 1994 Microsoft Corporation + +Module Name: + + PerSeat.h + +Abstract: + + +Author: + + Arthur Hanson (arth) Dec 07, 1994 + +Environment: + +Revision History: + + Jeff Parham (jeffparh) 12-Jan-1996 + o Added support for maintaining the SUITE_USE flag when adding + users to the AddCache. + o Exported function prototype for MappingLicenseListFree(). + +--*/ + +#ifndef _LLS_PERSEAT_H +#define _LLS_PERSEAT_H + + +#ifdef __cplusplus +extern "C" { +#endif + +#define DATA_TYPE_USERNAME 0 +#define DATA_TYPE_SID 1 + +#define MAX_ACCESS_COUNT 0xFFFFFFF + + +///////////////////////////////////////////////////////////////////////// +// +// Add cache is here as add records need exclusive access to the user table. +// Since we may have alot of read requests comming in at once, we don't want +// to hold up our reply waiting for the exclusive access to be granted, so +// we just dump it onto the add cache (queue) and continue on. +// +// This is even more important with outgoing replication going on as we can +// have shared access lock on the table for awhile. +// +// Incomming replication just bundles up the data and sticks it on the Add +// Cache to be processed like normal requests. +// +struct _ADD_CACHE; + +typedef struct _ADD_CACHE { + struct _ADD_CACHE *prev; + ULONG DataType; + ULONG DataLength; + PVOID Data; + PMASTER_SERVICE_RECORD Service; + ULONG AccessCount; + DWORD LastAccess; + DWORD Flags; +} ADD_CACHE, *PADD_CACHE; + + +///////////////////////////////////////////////////////////////////////// +// +// These records are for storing the actual user-useage information. +// +typedef struct _USER_LICENSE_RECORD { + DWORD Flags; + PMASTER_SERVICE_ROOT Family; + ULONG RefCount; + + // + // Version of product License applies to + PMASTER_SERVICE_RECORD Service; + ULONG LicensesNeeded; +} USER_LICENSE_RECORD, *PUSER_LICENSE_RECORD; + +typedef struct _SVC_RECORD { + // + // Actual service this is for + // + PMASTER_SERVICE_RECORD Service; + + // + // What license we took - The product may be SQL 3.0, but in determining + // the license we might have grabbed a SQL 4.0 license... + // + PUSER_LICENSE_RECORD License; + + ULONG AccessCount; + DWORD LastAccess; + ULONG Suite; + DWORD Flags; +} SVC_RECORD, *PSVC_RECORD; + +typedef struct _USER_RECORD { + ULONG IDSize; + PVOID UserID; + + // + // Pointer to mapping to use. + // + PMAPPING_RECORD Mapping; + + // + // Flags is mostly used right now for marking records to be deleted and + // if backoffice has been set. + // + DWORD Flags; + + // + // How many products are licensed vs unlicensed + // + ULONG LicensedProducts; + + // + // Date when last replicated. Note: For SID records this is a pointer + // into the USER_RECORD for the appropriate user. + // + ULONG LastReplicated; + + // + // Keep only a critical section lock, and not RTL_RESOURCE (for read/write + // locks). All updates of existing services (most common case by far) will + // be very quick, so exclusive access isn't that bad. RTL_RESOURCE also + // would make our table size increase dramatically and add too much extra + // processing. + // + RTL_CRITICAL_SECTION ServiceTableLock; + + // + // Service table is a linear buffer, we use the service number to access + // into this buffer. + // + ULONG ServiceTableSize; + + // Stuff per service - linear buffer... + PSVC_RECORD Services; + + // + // Licenses the user is using + // + ULONG LicenseListSize; + PUSER_LICENSE_RECORD *LicenseList; +} USER_RECORD, *PUSER_RECORD; + + +///////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////// + +extern ULONG UserListNumEntries; +extern ULONG SidListNumEntries; +extern RTL_GENERIC_TABLE UserList; +extern RTL_GENERIC_TABLE SidList; + +extern RTL_RESOURCE UserListLock; +extern RTL_RESOURCE SidListLock; + +// +// The enum processes for replication and UI can take awhile to go through +// all the records, while doing this they need a shared lock on the file. +// However, if we request an exclusive access during this time the pending +// exclusive access will block other shared access's. Therefore we get +// this lock first before attempting either an add or enum. +// +// An add will block enums, but neither of these function are as time +// critical as updating normal user records. +// +extern RTL_RESOURCE UserListAddEnumLock; +extern RTL_RESOURCE SidListAddEnumLock; + +// +// The AddCache itself, a critical section to protect access to it and an +// event to signal the server when there are items on it that need to be +// processed. +// +extern PADD_CACHE AddCache; +extern ULONG AddCacheSize; +extern RTL_CRITICAL_SECTION AddCacheLock; +extern HANDLE LLSAddCacheEvent; + +extern DWORD LastUsedTime; +extern BOOL UsersDeleted; + + +///////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////// + +PSVC_RECORD SvcListFind( LPTSTR DisplayName, PSVC_RECORD ServiceList, ULONG NumTableEntries ); +NTSTATUS SvcListDelete( LPTSTR UserName, LPTSTR ServiceName ); +VOID SvcListLicenseFree( PUSER_RECORD pUser ); +VOID SvcListLicenseUpdate( PUSER_RECORD pUser ); + +VOID UserListInit(); +VOID UserListUpdate( ULONG DataType, PVOID Data, PSERVICE_RECORD Service ); +PUSER_RECORD UserListFind( LPTSTR UserName ); + +VOID UserBackOfficeCheck( PUSER_RECORD pUser ); + +VOID UserListLicenseDelete( PMASTER_SERVICE_RECORD Service, LONG Quantity ); +VOID UserLicenseListFree ( PUSER_RECORD pUser ); + +VOID UserMappingAdd ( PMAPPING_RECORD Mapping, PUSER_RECORD pUser ); +VOID FamilyLicenseUpdate ( PMASTER_SERVICE_ROOT Family ); +VOID SvcLicenseUpdate( PUSER_RECORD pUser, PSVC_RECORD Svc ); + +VOID MappingLicenseListFree ( PMAPPING_RECORD Mapping ); +VOID MappingLicenseUpdate ( PMAPPING_RECORD Mapping, BOOL ReSynch ); + + +///////////////////////////////////////////////////////////////////////// +#if DBG + +VOID AddCacheDebugDump( ); +VOID UserListDebugDump( ); +VOID UserListDebugInfoDump( PVOID Data ); +VOID UserListDebugFlush( ); +VOID SidListDebugDump( ); +VOID SidListDebugInfoDump( PVOID Data ); +VOID SidListDebugFlush( ); + +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/private/net/svcdlls/lls/server/purchase.c b/private/net/svcdlls/lls/server/purchase.c new file mode 100644 index 000000000..17720ac82 --- /dev/null +++ b/private/net/svcdlls/lls/server/purchase.c @@ -0,0 +1,865 @@ +/*++ + +Copyright (c) 1995 Microsoft Corporation + +Module Name: + + purchase.c + +Abstract: + + +Author: + + Arthur Hanson (arth) 03-Jan-1995 + +Revision History: + + Jeff Parham (jeffparh) 05-Dec-1995 + o Added support for uniting per seat and per server purchase models. + o Added extra parameters and code to support secure certificates and + certificate database. + +--*/ + +#include <stdlib.h> +#include <nt.h> +#include <ntrtl.h> +#include <nturtl.h> +#include <windows.h> + +#include <debug.h> +#include "llsapi.h" +#include "llsutil.h" +#include "llssrv.h" +#include "mapping.h" +#include "msvctbl.h" +#include "purchase.h" +#include "svctbl.h" +#include "perseat.h" +#include "registry.h" +#include "llsrpc_s.h" +#include "certdb.h" +#include "server.h" + + +ULONG LicenseServiceListSize = 0; +PLICENSE_SERVICE_RECORD *LicenseServiceList = NULL; + +ULONG PerServerLicenseServiceListSize = 0; +PLICENSE_SERVICE_RECORD *PerServerLicenseServiceList = NULL; + +PLICENSE_PURCHASE_RECORD PurchaseList = NULL; +ULONG PurchaseListSize = 0; + +RTL_RESOURCE LicenseListLock; + + +static +NTSTATUS +LicenseAdd_UpdateQuantity( + LPTSTR ServiceName, + LONG Quantity, + BOOL UsePerServerList, + PLICENSE_SERVICE_RECORD * ppService, + BOOL * pChangeLicense, + LONG * pNewLicenses, + PMASTER_SERVICE_RECORD * pmService + ); + + + +///////////////////////////////////////////////////////////////////////// +VOID +LicenseListInit() + +/*++ + +Routine Description: + +Arguments: + + None. + +Return Value: + + None. + +--*/ + +{ + RtlInitializeResource(&LicenseListLock); + +} // LicenseListInit + + +///////////////////////////////////////////////////////////////////////// +int __cdecl LicenseServiceListCompare(const void *arg1, const void *arg2) { + PLICENSE_SERVICE_RECORD Svc1, Svc2; + + Svc1 = (PLICENSE_SERVICE_RECORD) *((PLICENSE_SERVICE_RECORD *) arg1); + Svc2 = (PLICENSE_SERVICE_RECORD) *((PLICENSE_SERVICE_RECORD *) arg2); + + return lstrcmpi( Svc1->ServiceName, Svc2->ServiceName); + +} // LicenseServiceListCompare + + +///////////////////////////////////////////////////////////////////////// +PLICENSE_SERVICE_RECORD +LicenseServiceListFind( + LPTSTR ServiceName, + BOOL UsePerServerList + ) + +/*++ + +Routine Description: + +Arguments: + + ServiceName - + + (JeffParh 95-10-31) + UsePerServerList - Determines whether the license record is searched for + in the per seat list (as in 3.51) or in the per server list (new for + SUR). The license purchase models are now unified, so there is now + a purchase history for both per seat and per server licenses. + +Return Value: + + Pointer to found service table entry or NULL if not found. + +--*/ + +{ + LONG begin = 0; + LONG end = (LONG) LicenseServiceListSize - 1; + LONG cur; + int match; + PLICENSE_SERVICE_RECORD Service; + PLICENSE_SERVICE_RECORD * ServiceList; + +#if DBG + if (TraceFlags & TRACE_FUNCTION_TRACE) + dprintf(TEXT("LLS TRACE: LicenseServiceListFind\n")); +#endif + if (ServiceName == NULL) + return NULL; + + if ( UsePerServerList ) + { + end = PerServerLicenseServiceListSize - 1; + ServiceList = PerServerLicenseServiceList; + } + else + { + end = LicenseServiceListSize - 1; + ServiceList = LicenseServiceList; + } + + while (end >= begin) { + // go halfway in-between + cur = (begin + end) / 2; + Service = ServiceList[cur]; + + // compare the two result into match + match = lstrcmpi(ServiceName, Service->ServiceName); + + if (match < 0) + // move new begin + end = cur - 1; + else + begin = cur + 1; + + if (match == 0) + return Service; + } + + return NULL; + +} // LicenseServiceListFind + + +///////////////////////////////////////////////////////////////////////// +PLICENSE_SERVICE_RECORD +LicenseServiceListAdd( + LPTSTR ServiceName, + BOOL UsePerServerList + ) + +/*++ + +Routine Description: + + +Arguments: + + ServiceName - + + (JeffParh 95-10-31) + UsePerServerList - Determines whether the license record is added to + the per seat list (as in 3.51) or the per server list (new for + SUR). The license purchase models are now unified, so there is now + a purchase history for both per seat and per server licenses. + +Return Value: + + Pointer to added service table entry, or NULL if failed. + +--*/ + +{ + PLICENSE_SERVICE_RECORD Service; + LPTSTR NewServiceName; + PLICENSE_SERVICE_RECORD ** pServiceList; + LPDWORD pServiceListSize; + +#if DBG + if (TraceFlags & TRACE_FUNCTION_TRACE) + dprintf(TEXT("LLS TRACE: LicenseServiceListAdd\n")); +#endif + // + // We do a double check here to see if another thread just got done + // adding the service, between when we checked last and actually got + // the write lock. + // + Service = LicenseServiceListFind(ServiceName, UsePerServerList); + if (Service != NULL) { + return Service; + } + + if ( UsePerServerList ) + { + pServiceList = &PerServerLicenseServiceList; + pServiceListSize = &PerServerLicenseServiceListSize; + } + else + { + pServiceList = &LicenseServiceList; + pServiceListSize = &LicenseServiceListSize; + } + + // + // Allocate space for table (zero init it). + // + if (*pServiceList == NULL) + *pServiceList = (PLICENSE_SERVICE_RECORD *) LocalAlloc(LPTR, sizeof(PLICENSE_SERVICE_RECORD) * (*pServiceListSize + 1)); + else + *pServiceList = (PLICENSE_SERVICE_RECORD *) LocalReAlloc(*pServiceList, sizeof(PLICENSE_SERVICE_RECORD) * (*pServiceListSize + 1), LHND); + + // + // Make sure we could allocate service table + // + if (*pServiceList == NULL) { + ASSERT(FALSE); + *pServiceListSize = 0; + return NULL; + } + + Service = (PLICENSE_SERVICE_RECORD) LocalAlloc(LPTR, sizeof(LICENSE_SERVICE_RECORD)); + if (Service == NULL) { + ASSERT(FALSE); + return NULL; + } + + // + // Create space for saving off the name. + // + NewServiceName = (LPTSTR) LocalAlloc(LPTR, (lstrlen(ServiceName) + 1) * sizeof(TCHAR)); + if (NewServiceName == NULL) { + ASSERT(FALSE); + return NULL; + } + + // now copy it over... + Service->ServiceName = NewServiceName; + lstrcpy(NewServiceName, ServiceName); + + (*pServiceList)[*pServiceListSize] = Service; + Service->NumberLicenses = 0; + Service->Index = *pServiceListSize; + (*pServiceListSize)++; + + // Have added the entry - now need to sort it in order of the service names + qsort((void *) *pServiceList, (size_t) *pServiceListSize, sizeof(PLICENSE_SERVICE_RECORD), LicenseServiceListCompare); + + return Service; + +} // LicenseServiceListAdd + + +///////////////////////////////////////////////////////////////////////// +ULONG +ProductLicensesGet( + LPTSTR ServiceName, + BOOL UsePerServerList + ) + +/*++ + +Routine Description: + + +Arguments: + + ServiceName - + + (JeffParh 95-10-31) + UsePerServerList - Determines whether the number of licenses is retirved + from the per seat list (as in 3.51) or the per server list (new for + SUR). The license purchase models are now unified, so there is now + a purchase history for both per seat and per server licenses. + +Return Value: + + +--*/ + +{ + PLICENSE_SERVICE_RECORD Service; + ULONG NumLicenses = 0; + +#if DBG + if (TraceFlags & TRACE_FUNCTION_TRACE) + dprintf(TEXT("LLS TRACE: ProductLicenseGet\n")); +#endif + + // + // Try to find the service. + // + RtlAcquireResourceShared(&LicenseListLock, TRUE); + Service = LicenseServiceListFind(ServiceName, UsePerServerList); + if (Service != NULL) + NumLicenses = Service->NumberLicenses; + RtlReleaseResource(&LicenseListLock); + + return NumLicenses; +} // ProductLicensesGet + + +///////////////////////////////////////////////////////////////////////// +NTSTATUS +LicenseAdd( + LPTSTR ServiceName, + LPTSTR Vendor, + LONG Quantity, + DWORD MaxQuantity, + LPTSTR Admin, + LPTSTR Comment, + DWORD Date, + DWORD AllowedModes, + DWORD CertificateID, + LPTSTR Source, + DWORD ExpirationDate, + LPDWORD Secrets + ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ + +{ + static DWORD NullSecrets[ LLS_NUM_SECRETS ] = { 0, 0, 0, 0 }; + + BOOL ChangeLicense = FALSE; + PLICENSE_SERVICE_RECORD Service = NULL; + PLICENSE_PURCHASE_RECORD PurchaseRecord; + LONG NewLicenses = 0; + NTSTATUS Status; + BOOL PerServerChangeLicense = FALSE; + PLICENSE_SERVICE_RECORD PerServerService = NULL; + LONG PerServerNewLicenses = 0; + LPTSTR NewName; + PMASTER_SERVICE_RECORD mService; + LLS_LICENSE_INFO_1 lic; + +#if DBG + if (TraceFlags & TRACE_FUNCTION_TRACE) + dprintf(TEXT("LLS TRACE: LicenseAdd\n")); +#endif + + if ( ( 0 == CertificateID ) && ( ServiceIsSecure( ServiceName ) ) ) + { + return STATUS_ACCESS_DENIED; + } + + if ( ( 0 != ExpirationDate ) && ( ExpirationDate < DateSystemGet() ) ) + { + // certificate has expired + return STATUS_ACCOUNT_EXPIRED; + } + + if ( ( NULL == ServiceName ) + || ( NULL == Vendor ) + || ( 0 == Quantity ) + || ( ( 0 != CertificateID ) && ( 0 == MaxQuantity ) ) + || ( NULL == Admin ) + || ( NULL == Comment ) + || ( 0 == ( AllowedModes & ( LLS_LICENSE_MODE_ALLOW_PER_SEAT | LLS_LICENSE_MODE_ALLOW_PER_SERVER ) ) ) + || ( NULL == Source ) ) + { + // invalid parameter + return STATUS_INVALID_PARAMETER; + } + + if ( NULL == Secrets ) + { + Secrets = NullSecrets; + } + + RtlAcquireResourceExclusive(&LicenseListLock, TRUE); + + if ( 0 != CertificateID ) + { + lic.Product = ServiceName; + lic.Vendor = Vendor; + lic.Quantity = Quantity; + lic.MaxQuantity = MaxQuantity; + lic.Admin = Admin; + lic.Comment = Comment; + lic.Date = Date; + lic.AllowedModes = AllowedModes; + lic.CertificateID = CertificateID; + lic.Source = Source; + lic.ExpirationDate = ExpirationDate; + memcpy( lic.Secrets, Secrets, LLS_NUM_SECRETS * sizeof( *Secrets ) ); + + if ( !CertDbClaimApprove( &lic ) ) + { + // no way, hoser! + RtlReleaseResource( &LicenseListLock ); + return STATUS_OBJECT_NAME_EXISTS; + } + } + + // update totals for per seat / per server mode licenses + + Status = STATUS_SUCCESS; + + if ( AllowedModes & 1 ) + { + // per seat allowed; add to per seat license tally + Status = LicenseAdd_UpdateQuantity( ServiceName, + Quantity, + FALSE, + &Service, + &ChangeLicense, + &NewLicenses, + &mService ); + } + + if ( ( STATUS_SUCCESS == Status ) && ( AllowedModes & 2 ) ) + { + // per server allowed; add to per server license tally + Status = LicenseAdd_UpdateQuantity( ServiceName, + Quantity, + TRUE, + &PerServerService, + &PerServerChangeLicense, + &PerServerNewLicenses, + &mService ); + } + + if ( STATUS_SUCCESS != Status ) + { + RtlReleaseResource( &LicenseListLock ); + return Status; + } + + // + // Service now points to the service table entry - now update purchase + // History. + // + if (PurchaseList == NULL) + PurchaseList = (PLICENSE_PURCHASE_RECORD) LocalAlloc(LPTR, sizeof(LICENSE_PURCHASE_RECORD) * (PurchaseListSize + 1)); + else + PurchaseList = (PLICENSE_PURCHASE_RECORD) LocalReAlloc(PurchaseList, sizeof(LICENSE_PURCHASE_RECORD) * (PurchaseListSize + 1), LHND); + + // + // Make sure we could allocate service table + // + if (PurchaseList == NULL) + { + ASSERT(FALSE); + PurchaseListSize = 0; + RtlReleaseResource(&LicenseListLock); + return STATUS_NO_MEMORY; + } + + PurchaseRecord = &PurchaseList[PurchaseListSize]; + + // + // Create space for saving off the Admin Name + // + NewName = (LPTSTR) LocalAlloc(LPTR, (lstrlen(Admin) + 1) * sizeof(TCHAR)); + if (NewName == NULL) + { + ASSERT(FALSE); + RtlReleaseResource(&LicenseListLock); + return STATUS_NO_MEMORY; + } + + // now copy it over... + PurchaseRecord->Admin = NewName; + lstrcpy(NewName, Admin); + + // + // Create space for saving off the Comment + // + NewName = (LPTSTR) LocalAlloc(LPTR, (lstrlen(Comment) + 1) * sizeof(TCHAR)); + if (NewName == NULL) + { + ASSERT(FALSE); + RtlReleaseResource(&LicenseListLock); + return STATUS_NO_MEMORY; + } + + // now copy it over... + PurchaseRecord->Comment = NewName; + lstrcpy(NewName, Comment); + + // + // Create space for saving off the Source + // + NewName = (LPTSTR) LocalAlloc(LPTR, (lstrlen(Source) + 1) * sizeof(TCHAR)); + if (NewName == NULL) + { + ASSERT(FALSE); + RtlReleaseResource(&LicenseListLock); + return STATUS_NO_MEMORY; + } + + // now copy it over... + PurchaseRecord->Source = NewName; + lstrcpy(NewName, Source); + + // + // Create space for saving off the Vendor + // + NewName = (LPTSTR) LocalAlloc(LPTR, (lstrlen(Vendor) + 1) * sizeof(TCHAR)); + if (NewName == NULL) + { + ASSERT(FALSE); + RtlReleaseResource(&LicenseListLock); + return STATUS_NO_MEMORY; + } + + // now copy it over... + PurchaseRecord->Vendor = NewName; + lstrcpy(NewName, Vendor); + + // + // Update the rest of the stuff. + // + PurchaseRecord->NumberLicenses = Quantity; + PurchaseRecord->MaxQuantity = MaxQuantity; + PurchaseRecord->Service = Service; + PurchaseRecord->PerServerService = PerServerService; + PurchaseRecord->AllowedModes = AllowedModes; + PurchaseRecord->CertificateID = CertificateID; + PurchaseRecord->ExpirationDate = ExpirationDate; + memcpy( PurchaseRecord->Secrets, Secrets, LLS_NUM_SECRETS * sizeof( *Secrets ) ); + + if (Date == 0) + PurchaseRecord->Date = DateSystemGet(); + else + PurchaseRecord->Date = Date; + + PurchaseListSize++; + + RtlReleaseResource(&LicenseListLock); + + if ( 0 != CertificateID ) + { + // these should still be set from above + ASSERT( lic.Product == ServiceName ); + ASSERT( lic.Vendor == Vendor ); + ASSERT( lic.Quantity == Quantity ); + ASSERT( lic.MaxQuantity == MaxQuantity ); + ASSERT( lic.Admin == Admin ); + ASSERT( lic.Comment == Comment ); + ASSERT( lic.Date == Date ); + ASSERT( lic.AllowedModes == AllowedModes ); + ASSERT( lic.CertificateID == CertificateID ); + ASSERT( lic.Source == Source ); + ASSERT( lic.ExpirationDate == ExpirationDate ); + ASSERT( 0 == memcmp( PurchaseRecord->Secrets, Secrets, LLS_NUM_SECRETS * sizeof( *Secrets ) ) ); + + CertDbClaimEnter( NULL, &lic, FALSE, 0 ); + } + + // + // Now check if we need to re-scan the user list and update licenses + // + if (ChangeLicense) + { + if ( NewLicenses < 0 ) + UserListLicenseDelete( mService, NewLicenses ); + + if ( NewLicenses > 0 ) + FamilyLicenseUpdate ( mService->Family ); + } + + if ( AllowedModes & 2 ) + { + // per server licenses modified + LocalServiceListConcurrentLimitSet(); + LocalServerServiceListUpdate(); + ServiceListResynch(); + } + + return STATUS_SUCCESS; + +} // LicenseAdd + + +///////////////////////////////////////////////////////////////////////// +static +NTSTATUS +LicenseAdd_UpdateQuantity( + LPTSTR ServiceName, + LONG Quantity, + BOOL UsePerServerList, + PLICENSE_SERVICE_RECORD * ppService, + BOOL * pChangeLicense, + LONG * pNewLicenses, + PMASTER_SERVICE_RECORD * pmService + ) +{ + BOOL ChangeLicense = FALSE; + PLICENSE_SERVICE_RECORD Service; + PLICENSE_PURCHASE_RECORD PurchaseRecord; + PMASTER_SERVICE_RECORD mService; + LONG NewLicenses = 0; + + Service = LicenseServiceListFind( ServiceName, UsePerServerList ); + + // + // If we didn't find it we will need to add it. + // + if (Service == NULL) + { + if (Quantity < 0) + { +#if DBG + dprintf(TEXT("Releasing Licenses from Non-existant product!\n")); +#endif + // ASSERT(FALSE); + return STATUS_UNSUCCESSFUL; + } + else + { + Service = LicenseServiceListAdd(ServiceName, UsePerServerList); + } + } + + // + // Check to make sure we found or added it. The only way we can fail is + // if we couldn't alloc memory for it. + // + if (Service == NULL) + { + ASSERT(FALSE); + return STATUS_NO_MEMORY; + } + + if (((LONG) Service->NumberLicenses + Quantity) < 0) + { + return STATUS_UNSUCCESSFUL; + } + + // + // Update license count in service record + // + Service->NumberLicenses += Quantity; + + // + // Now in master Service Record + // + RtlAcquireResourceShared(&MasterServiceListLock, TRUE); + mService = MasterServiceListFind(ServiceName); + + if (mService != NULL) + { + // + // if we were out of license and added more, then we need to update + // the user list. + // + if ( ( Quantity > 0 ) + && ( (mService->LicensesUsed > mService->Licenses) || (mService == BackOffice) ) ) + { + ChangeLicense = TRUE; + } + + // + // Can only add a number of licenses up to licensed amount + // + if ( ChangeLicense ) + { + // Get current unlicensed delta + NewLicenses = mService->LicensesUsed - mService->Licenses; + + if ((NewLicenses <= 0) || (NewLicenses > Quantity)) + { + NewLicenses = Quantity; + } + } + + if ( UsePerServerList ) + { + // this will be done by LicenseAdd() in LocalServerServiceListUpdate() + // mService->MaxSessionCount += Quantity; + } + else + { + mService->Licenses += Quantity; + } + + // + // if we we subtracted licenses and are out of licenses, then we + // need to scan the user list. + // + if (Quantity < 0) + { + if (mService->LicensesUsed > mService->Licenses) + { + ChangeLicense = TRUE; + + // + // Only remove # of licenses past limit + // + NewLicenses = mService->Licenses - mService->LicensesUsed; + if (NewLicenses < Quantity) + { + NewLicenses = Quantity; + } + } + } + } + + RtlReleaseResource(&MasterServiceListLock); + + *ppService = Service; + *pChangeLicense = ChangeLicense; + *pNewLicenses = NewLicenses; + *pmService = mService; + + return STATUS_SUCCESS; +} + +#if DBG +///////////////////////////////////////////////////////////////////////// +VOID +LicenseListDebugDump( ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ + +{ + ULONG i = 0; + ULONG j = 0; + + // + // Need to scan list so get read access. + // + RtlAcquireResourceShared(&LicenseListLock, TRUE); + + // + // Dump License Service List first + // + dprintf(TEXT("Per Seat License Service Table, # Entries: %lu\n"), LicenseServiceListSize); + if (LicenseServiceList != NULL) + { + for (i = 0; i < LicenseServiceListSize; i++) + dprintf(TEXT(" %2lu) Tot Licenses: %lu Product: %s\n"), i, LicenseServiceList[i]->NumberLicenses, LicenseServiceList[i]->ServiceName); + } + + dprintf(TEXT("\nPer Server License Service Table, # Entries: %lu\n"), PerServerLicenseServiceListSize); + if (PerServerLicenseServiceList != NULL) + { + for (i = 0; i < PerServerLicenseServiceListSize; i++) + dprintf(TEXT(" %2lu) Tot Licenses: %lu Product: %s\n"), i, PerServerLicenseServiceList[i]->NumberLicenses, PerServerLicenseServiceList[i]->ServiceName); + } + + // + // Now do purchase history + // + dprintf(TEXT("\nPurchase History, # Entries: %lu\n"), PurchaseListSize); + if (PurchaseList != NULL) + { + for (i = 0; i < PurchaseListSize; i++) + { + TCHAR szExpirationDate[ 40 ]; + + lstrcpy( szExpirationDate, TimeToString( PurchaseList[i].ExpirationDate ) ); + + dprintf( TEXT(" %3lu) Product : %s\n" ) + TEXT( " Vendor : %s\n" ) + TEXT( " Allowed Modes :%s%s\n" ) + TEXT( " Licenses : %d\n" ) + TEXT( " Max Licenses : %lu\n" ) + TEXT( " Date Entered : %s\n" ) + TEXT( " Date Expires : %s\n" ) + TEXT( " Certificate ID : %lu\n" ) + TEXT( " Secrets :" ), + i, + ( PurchaseList[i].AllowedModes & LLS_LICENSE_MODE_ALLOW_PER_SEAT ) + ? PurchaseList[i].Service->ServiceName + : PurchaseList[i].PerServerService->ServiceName, + PurchaseList[i].Vendor, + ( PurchaseList[i].AllowedModes & LLS_LICENSE_MODE_ALLOW_PER_SEAT ) + ? TEXT(" PERSEAT") + : TEXT(""), + ( PurchaseList[i].AllowedModes & LLS_LICENSE_MODE_ALLOW_PER_SERVER ) + ? TEXT(" PERSERVER") + : TEXT(""), + PurchaseList[i].NumberLicenses, + PurchaseList[i].MaxQuantity, + TimeToString( PurchaseList[i].Date ), + szExpirationDate, + PurchaseList[i].CertificateID + ); + + for ( j=0; j < LLS_NUM_SECRETS; j++ ) + { + dprintf( TEXT( " %08X" ), PurchaseList[i].Secrets[j] ); + } + + dprintf( TEXT( "\n" ) + TEXT( " Source : %s\n" ) + TEXT( " Admin : %s\n" ) + TEXT( " Comment : %s\n\n" ), + PurchaseList[i].Source, + PurchaseList[i].Admin, + PurchaseList[i].Comment + ); + } + } + +LicenseListDebugDumpExit: + RtlReleaseResource(&LicenseListLock); + + return; +} // LicenseListDebugDump + +#endif diff --git a/private/net/svcdlls/lls/server/purchase.h b/private/net/svcdlls/lls/server/purchase.h new file mode 100644 index 000000000..1e18b4879 --- /dev/null +++ b/private/net/svcdlls/lls/server/purchase.h @@ -0,0 +1,114 @@ +/*++ + +Copyright (c) 1995 Microsoft Corporation + +Module Name: + + Purchase.h + +Abstract: + + +Author: + + Arthur Hanson (arth) Dec 07, 1994 + +Environment: + +Revision History: + + Jeff Parham (jeffparh) 05-Dec-1995 + o Added support for uniting per seat and per server purchase models. + o Added extra parameters and code to support secure certificates and + certificate database. + +--*/ + +#ifndef _LLS_PURCHASE_H +#define _LLS_PURCHASE_H + + +#ifdef __cplusplus +extern "C" { +#endif + + +typedef struct _LICENSE_SERVICE_RECORD { + LPTSTR ServiceName; + ULONG Index; + LONG NumberLicenses; +} LICENSE_SERVICE_RECORD, *PLICENSE_SERVICE_RECORD; + + +typedef struct _LICENSE_PURCHASE_RECORD { + PLICENSE_SERVICE_RECORD Service; + LONG NumberLicenses; + DWORD Date; + LPTSTR Admin; + LPTSTR Comment; + + // added for SUR: + PLICENSE_SERVICE_RECORD PerServerService; // points to per server + // license tally for this + // service + + DWORD AllowedModes; // bit field: 1, allowed + // to be used in per seat + // mode; 2, per server + + DWORD CertificateID; // identifies the secure + // certificate from which + // these licenses came, or + // 0 if unsecure + + LPTSTR Source; // source of the certificate + // currently supported + // values are "None" and + // "Paper" + + DWORD ExpirationDate; // time at which this + // certificate expires + + DWORD MaxQuantity; // the largest number of licenses + // that can be installed from this + // certificate + + LPTSTR Vendor; // vendor of the product, e.g., + // "Microsoft" + + DWORD Secrets[ LLS_NUM_SECRETS ]; // secrets for LSAPI + // challenge mechanism + +} LICENSE_PURCHASE_RECORD, *PLICENSE_PURCHASE_RECORD; + + + +extern ULONG LicenseServiceListSize; +extern PLICENSE_SERVICE_RECORD *LicenseServiceList; + +extern ULONG PerServerLicenseServiceListSize; +extern PLICENSE_SERVICE_RECORD *PerServerLicenseServiceList; + +extern PLICENSE_PURCHASE_RECORD PurchaseList; +extern ULONG PurchaseListSize; + +extern RTL_RESOURCE LicenseListLock; + + +VOID LicenseListInit(); +PLICENSE_SERVICE_RECORD LicenseServiceListFind( LPTSTR ServiceName, BOOL UsePerServerList ); +PLICENSE_SERVICE_RECORD LicenseServiceListAdd( LPTSTR ServiceName, BOOL UsePerServerList ); +ULONG ProductLicensesGet( LPTSTR ServiceName, BOOL UsePerServerList ); +NTSTATUS LicenseAdd( LPTSTR ServiceName, LPTSTR Vendor, LONG Quantity, DWORD MaxQuantity, LPTSTR Admin, LPTSTR Comment, DWORD Date, DWORD AllowedModes, DWORD CertificateID, LPTSTR Source, DWORD ExpirationDate, LPDWORD Secrets ); + + +#if DBG +VOID LicenseListDebugDump( ); +#endif + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/private/net/svcdlls/lls/server/registry.c b/private/net/svcdlls/lls/server/registry.c new file mode 100644 index 000000000..622019b26 --- /dev/null +++ b/private/net/svcdlls/lls/server/registry.c @@ -0,0 +1,1707 @@ +/*++ + +Copyright (c) 1994 Microsoft Corporation + +Module Name: + + registry.c + +Abstract: + + Registry reading routines for License Server. Can Scan the registry + for all License Service entries, or for a specific service. + +Author: + + Arthur Hanson (arth) 07-Dec-1994 + +Revision History: + + Jeff Parham (jeffparh) 05-Dec-1995 + o Removed unnecessary RegConnect() to local server. + o Added secure service list. This list tracks the products that + require "secure" license certificates for all licenses; i.e., the + products that do not accept the 3.51 Honesty method of "enter the + number of license you purchased." + o Added routine to update the concurrent limit value in the registry + to accurately reflect the connection limit of secure products. + +--*/ + +#include <stdlib.h> +#include <nt.h> +#include <ntrtl.h> +#include <nturtl.h> +#include <windows.h> +#include <rpc.h> +#include <rpcndr.h> + +#include "llsapi.h" +#include "debug.h" +#include "llssrv.h" +#include "registry.h" +#include "ntlsapi.h" +#include "mapping.h" +#include "msvctbl.h" +#include "svctbl.h" +#include "purchase.h" +#include "perseat.h" +#include "server.h" +#include "llsutil.h" + +// #define API_TRACE 1 + +#define NUM_MAPPING_ENTRIES 2 +LPTSTR NameMappingTable[] = { + TEXT("SQL"), + TEXT("SNA") +}; // NameMappingTable + + +LPTSTR NameMappingTable2[] = { + TEXT("Microsoft SQL Server"), + TEXT("Microsoft SNA Server") +}; // NameMappingTable2 + + +ULONG NumFilePrintEntries = 0; +LPTSTR *FilePrintTable = NULL; + +#define KEY_NAME_SIZE 512 + +HANDLE LLSRegistryEvent; + + +ULONG LocalServiceListSize = 0; +PLOCAL_SERVICE_RECORD *LocalServiceList = NULL; + +RTL_RESOURCE LocalServiceListLock; + +ULONG SecureServiceListSize = 0; +LPTSTR * SecureServiceList = NULL; +ULONG SecureServiceBufferSize = 0; // in bytes! +TCHAR * SecureServiceBuffer = NULL; + + +///////////////////////////////////////////////////////////////////////// +VOID +ConfigInfoRegistryInit( + DWORD * pUseEnterprise, + LPTSTR pEnterpriseServer, + DWORD * pReplicationType, + DWORD * pReplicationTime, + DWORD * pLogLevel + ) +{ + HKEY hKey2 = NULL; + DWORD dwType, dwSize; + static BOOL ReportedError = FALSE; + static TCHAR RegKeyText[512]; + LONG Status; + BOOL ret = FALSE; + DWORD UseEnterprise, ReplicationType, ReplicationTime; + static TCHAR EnterpriseServer[MAX_COMPUTERNAME_LENGTH + 3]; + DWORD LogLevel; + + lstrcpy(EnterpriseServer, TEXT("")); + UseEnterprise = ReplicationType = ReplicationTime = LogLevel = 0; + + // + // Create registry key-name we are looking for + // + lstrcpy(RegKeyText, TEXT("System\\CurrentControlSet\\Services\\LicenseService\\Parameters")); + + if ((Status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, RegKeyText, 0, KEY_READ, &hKey2)) == ERROR_SUCCESS) + { + dwSize = sizeof(UseEnterprise); + Status = RegQueryValueEx(hKey2, TEXT("UseEnterprise"), NULL, &dwType, (LPBYTE) &UseEnterprise, &dwSize); + if (Status == ERROR_SUCCESS) + *pUseEnterprise = UseEnterprise; + else + UseEnterprise = 0; + + + if (Status == ERROR_SUCCESS) + { + dwSize = sizeof(EnterpriseServer); + Status = RegQueryValueEx(hKey2, TEXT("EnterpriseServer"), NULL, &dwType, (LPBYTE) &EnterpriseServer, &dwSize); + + if (Status == ERROR_SUCCESS) + { + lstrcpy(pEnterpriseServer, EnterpriseServer); + } + else + { + lstrcpy(pEnterpriseServer, TEXT("")); + + if (!ReportedError) + { + ReportedError = TRUE; +#ifdef DEBUG + dprintf(TEXT("LLS: (WARNING) No registry parm for EnterpriseServer\n")); +#endif + } + } + + } + else + { + if (!ReportedError) + { + ReportedError = TRUE; +#ifdef DEBUG + dprintf(TEXT("LLS: (WARNING) No registry parm for UseEnterprise\n")); +#endif + } + } + + dwSize = sizeof(ReplicationType); + Status = RegQueryValueEx(hKey2, TEXT("ReplicationType"), NULL, &dwType, (LPBYTE) &ReplicationType, &dwSize); + + if (Status == ERROR_SUCCESS) + { + dwSize = sizeof(ReplicationTime); + Status = RegQueryValueEx(hKey2, TEXT("ReplicationTime"), NULL, &dwType, (LPBYTE) &ReplicationTime, &dwSize); + + if (Status == ERROR_SUCCESS) + { + *pReplicationType = ReplicationType; + *pReplicationTime = ReplicationTime; + } + else + { + if (!ReportedError) + { + ReportedError = TRUE; +#ifdef DEBUG + dprintf(TEXT("LLS: (WARNING) No registry parm for ReplicationTime\n")); +#endif + } + } + + } + else + { + if (!ReportedError) + { + ReportedError = TRUE; +#ifdef DEBUG + dprintf(TEXT("LLS: (WARNING) No registry parm for ReplicationType\n")); +#endif + } + } + + // LogLevel (REG_DWORD): determines how much info is dumped to the EventLog. + // Higher values imply more logging. Default: 0. + dwSize = sizeof( LogLevel ); + Status = RegQueryValueEx( hKey2, TEXT("LogLevel"), NULL, &dwType, (LPBYTE) &LogLevel, &dwSize); + if ( ERROR_SUCCESS == Status ) + *pLogLevel = LogLevel; + else + *pLogLevel = 0; + + // ProductData (REG_BINARY): an encrypted buffer of concatenated service names + // that determine which services need to have secure certificates + // for license entry + Status = RegQueryValueEx( hKey2, TEXT("ProductData"), NULL, &dwType, NULL, &dwSize ); + if ( ERROR_SUCCESS == Status ) + { + TCHAR * NewSecureServiceBuffer = NULL; + LPTSTR * NewSecureServiceList = NULL; + ULONG NewSecureServiceListSize = 0; + ULONG NewSecureServiceBufferSize; + + NewSecureServiceBufferSize = dwSize; + NewSecureServiceBuffer = LocalAlloc( LMEM_FIXED, NewSecureServiceBufferSize ); + + if ( NULL != NewSecureServiceBuffer ) + { + Status = RegQueryValueEx( hKey2, TEXT("ProductData"), NULL, &dwType, (LPBYTE) NewSecureServiceBuffer, &dwSize); + + if ( ERROR_SUCCESS == Status ) + { + Status = DeBlock( NewSecureServiceBuffer, dwSize ); + + if ( ( STATUS_SUCCESS == Status ) + && ( ( NULL == SecureServiceBuffer ) + || ( memcmp( NewSecureServiceBuffer, SecureServiceBuffer, dwSize ) ) ) ) + { + // process changes in secure product list + DWORD i; + DWORD ProductNdx; + + NewSecureServiceListSize = 0; + + // count number of product names contained in the buffer + for ( i=0; ( i < dwSize ) && ( NewSecureServiceBuffer[i] != TEXT( '\0' ) ); i++ ) + { + // skip to beginning of next product name + for ( ; ( i < dwSize ) && ( NewSecureServiceBuffer[i] != TEXT( '\0' ) ); i++ ); + i++; + + if ( i * sizeof( TCHAR) < dwSize ) + { + // properly null-terminated product name + NewSecureServiceListSize++; + } + } + + if ( 0 != NewSecureServiceListSize ) + { + NewSecureServiceList = LocalAlloc( LMEM_FIXED, sizeof( LPTSTR ) * NewSecureServiceListSize ); + + if ( NULL != NewSecureServiceList ) + { + for ( i = ProductNdx = 0; ProductNdx < NewSecureServiceListSize; ProductNdx++ ) + { + NewSecureServiceList[ ProductNdx ] = &NewSecureServiceBuffer[i]; + + // skip to beginning of next product name + for ( ; NewSecureServiceBuffer[i] != TEXT( '\0' ); i++ ); + i++; + } + + // new secure product list read successfully; use it + if ( NULL != SecureServiceBuffer ) + { + LocalFree( SecureServiceBuffer ); + } + if ( NULL != SecureServiceList ) + { + LocalFree( SecureServiceList ); + } + + SecureServiceBuffer = NewSecureServiceBuffer; + SecureServiceList = NewSecureServiceList; + SecureServiceListSize = NewSecureServiceListSize; + SecureServiceBufferSize = NewSecureServiceBufferSize; + } + } + } + } + } + + // free buffers if we aren't using them anymore + if ( ( NULL != NewSecureServiceList ) + && ( SecureServiceList != NewSecureServiceList ) ) + { + LocalFree( NewSecureServiceList ); + } + + if ( ( NULL != NewSecureServiceBuffer ) + && ( SecureServiceBuffer != NewSecureServiceBuffer ) ) + { + LocalFree( NewSecureServiceBuffer ); + } + } + + RegCloseKey(hKey2); + } + +} // ConfigInfoRegistryInit + + +///////////////////////////////////////////////////////////////////////// +VOID +FilePrintTableInit( + ) + +/*++ + +Routine Description: + + Builds up the FilePrint mapping table by enumerating the keys in the + registry init'd by the various install programs. + +Arguments: + + +Return Value: + + None. + +--*/ + +{ + HKEY hKey2; + static TCHAR RegKeyText[512]; + static TCHAR KeyText[KEY_NAME_SIZE], ClassText[KEY_NAME_SIZE]; + LONG Status; + DWORD index = 0; + DWORD KeySize, ClassSize, NumKeys, NumValue, MaxKey, MaxClass, MaxValue, MaxValueData, MaxSD; + FILETIME LastWrite; + +#if DBG + if (TraceFlags & TRACE_FUNCTION_TRACE) + dprintf(TEXT("LLS TRACE: FilePrintTableInit\n")); +#endif + // + // Create registry key-name we are looking for + // + lstrcpy(RegKeyText, TEXT("System\\CurrentControlSet\\Services\\LicenseService\\FilePrint")); + + if ((Status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, RegKeyText, 0, KEY_READ, &hKey2)) == ERROR_SUCCESS) { + // + // Find out how many sub-keys there are to intialize our table size. + // The table can still grow dynamically, this just makes having to + // realloc it a rare occurance. + // + ClassSize = KEY_NAME_SIZE; + Status = RegQueryInfoKey(hKey2, ClassText, &ClassSize, NULL, + &NumKeys, &MaxKey, &MaxClass, &NumValue, + &MaxValue, &MaxValueData, &MaxSD, &LastWrite); + + if (Status == ERROR_SUCCESS) { + FilePrintTable = (LPTSTR *) LocalAlloc(LPTR, sizeof(LPTSTR) * NumKeys); + + while ((Status == ERROR_SUCCESS) && (FilePrintTable != NULL)) { + // + // Double check in-case we need to expand the table. + // + if (index > NumKeys) { + NumKeys++; + FilePrintTable = (LPTSTR *) LocalReAlloc(FilePrintTable, sizeof(LPTSTR) * NumKeys, LHND); + } + + if (FilePrintTable != NULL) { + // + // Now read in the key name and add it to the table + // + KeySize = KEY_NAME_SIZE; + Status = RegEnumKeyEx(hKey2, index, KeyText, &KeySize, NULL, NULL, NULL, &LastWrite); + if (Status == ERROR_SUCCESS) { + // + // Allocate space in our table and copy the key + // + FilePrintTable[index] = (LPTSTR) LocalAlloc(LPTR, (KeySize + 1) * sizeof(TCHAR)); + + if (FilePrintTable[index] != NULL) { + lstrcpy(FilePrintTable[index], KeyText); + index++; + } else + Status = (LONG) GetLastError(); + + } + + } + } + } +#ifdef DEBUG + else { + dprintf(TEXT("LLS FilePrintTable Error: 0x%lx\n"), Status); + } +#endif + + RegCloseKey( hKey2 ); + } + + if (FilePrintTable != NULL) + NumFilePrintEntries = index; + else + NumFilePrintEntries = 0; + +} // FilePrintTableInit + + +///////////////////////////////////////////////////////////////////////// +NTSTATUS +RegistryMonitor ( + IN PVOID ThreadParameter + ) + +/*++ + +Routine Description: + + Watches for any changes in the Licensing Keys, and if any updates our + internal information. + +Arguments: + + ThreadParameter - Indicates how many active threads there currently + are. + +Return Value: + + None. + +--*/ + +{ + LONG Status = 0; + HKEY hKey2 = NULL; + NTSTATUS NtStatus = STATUS_SUCCESS; + static TCHAR RegKeyText[KEY_NAME_SIZE]; + + // + // Create registry key-name we are looking for + // + lstrcpy(RegKeyText, TEXT("System\\CurrentControlSet\\Services")); + + // + // Registry is already open so just find the correct keys. + // + if ((Status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, RegKeyText, 0, KEY_ALL_ACCESS, &hKey2)) != ERROR_SUCCESS) { +#if DBG + dprintf(TEXT("LLS RegistryMonitor - RegOpenKeyEx failed: 0x%lX\n"), Status); +#endif + return (NTSTATUS) Status; + } + + // + // Loop forever + // + for ( ; ; ) { + + Status = RegNotifyChangeKeyValue(hKey2, TRUE, REG_NOTIFY_CHANGE_LAST_SET, LLSRegistryEvent, TRUE); + if (Status != ERROR_SUCCESS) { +#if DBG + dprintf(TEXT("LLS RegNotifyChangeKeyValue Failed: %lu\n"), Status); +#endif + } + + NtStatus = NtWaitForSingleObject( LLSRegistryEvent, TRUE, NULL ); + + // + // Re-synch the lists + // + LocalServiceListUpdate(); + LocalServerServiceListUpdate(); + ServiceListResynch(); + ConfigInfoRegistryUpdate(); + LocalServiceListConcurrentLimitSet(); + + if (NtStatus != STATUS_SUCCESS) { +#if DBG + dprintf(TEXT("LLS Registry Event Notification Failed: %lu\n"), NtStatus); +#endif + // + // If we failed - sleep for 2 minutes before looping + // + Sleep(120000L); + } + } + + return NtStatus; + +} // RegistryMonitor + + +///////////////////////////////////////////////////////////////////////// +VOID +RegistryInit( ) + +/*++ + +Routine Description: + + Looks in registry for given service and sets values accordingly. + +Arguments: + +Return Value: + + None. + +--*/ + +{ + NTSTATUS Status; + static TCHAR RegKeyText[512]; + BOOL ret = FALSE; + DWORD Mode, ConcurrentLimit; + + Mode = 0; + ConcurrentLimit = 0; + + // + // Create a key to tell us about any changes in the registry + // + Status = NtCreateEvent( + &LLSRegistryEvent, + EVENT_QUERY_STATE | EVENT_MODIFY_STATE | SYNCHRONIZE, + NULL, + SynchronizationEvent, + FALSE + ); + + ASSERT(NT_SUCCESS(Status)); + + // + // Create the FilePrint table + // + FilePrintTableInit(); + +} // RegistryInit + + +///////////////////////////////////////////////////////////////////////// +VOID +RegistryStartMonitor( ) + +/*++ + +Routine Description: + + Looks in registry for given service and sets values accordingly. + +Arguments: + +Return Value: + + None. + +--*/ + +{ + HANDLE Thread; + DWORD Ignore; + + // + // Now dispatch a thread to watch for any registry changes + // + Thread = CreateThread( + NULL, + 0L, + (LPTHREAD_START_ROUTINE) RegistryMonitor, + 0L, + 0L, + &Ignore + ); + + +} // RegistryStartMonitor + + +///////////////////////////////////////////////////////////////////////// +VOID +RegistryInitValues( + LPTSTR ServiceName, + BOOL *PerSeatLicensing, + ULONG *SessionLimit + ) + +/*++ + +Routine Description: + + Looks in registry for given service and sets values accordingly. + +Arguments: + + Service Name - + + PerSeatLicensing - + + SessionLimit - + +Return Value: + + None. + +--*/ + +{ + HKEY hKey2 = NULL; + DWORD dwType, dwSize; + static TCHAR RegKeyText[512]; + LONG Status; + DWORD Mode, ConcurrentLimit; + +#if DBG + if (TraceFlags & TRACE_FUNCTION_TRACE) + dprintf(TEXT("LLS TRACE: RegistryInitValues\n")); +#endif + +#ifdef SPECIAL_USER_LIMIT + *PerSeatLicensing = FALSE; + *SessionLimit = SPECIAL_USER_LIMIT; +#else // #ifdef SPECIAL_USER_LIMIT + Mode = 0; + ConcurrentLimit = 0; + + // + // Create registry key-name we are looking for + // + lstrcpy(RegKeyText, TEXT("System\\CurrentControlSet\\Services\\LicenseInfo\\")); + lstrcat(RegKeyText, ServiceName); + + if ((Status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, RegKeyText, 0, KEY_READ, &hKey2)) == ERROR_SUCCESS) { + // + // First Get Mode + // + dwSize = sizeof(Mode); + Status = RegQueryValueEx(hKey2, TEXT("Mode"), NULL, &dwType, (LPBYTE) &Mode, &dwSize); + +#if DBG + if ((TraceFlags & TRACE_REGISTRY) && (Status == ERROR_SUCCESS)) + dprintf(TEXT("Found Reg-Key for [%s] Mode: %ld\n"), ServiceName, Mode); +#endif + // + // Now Concurrent Limit + // + dwSize = sizeof(ConcurrentLimit); + Status = RegQueryValueEx(hKey2, TEXT("ConcurrentLimit"), NULL, &dwType, (LPBYTE) &ConcurrentLimit, &dwSize); + +#if DBG + if ((TraceFlags & TRACE_REGISTRY) && (Status == ERROR_SUCCESS)) + dprintf(TEXT("Found Reg-Key for [%s] ConcurrentLimit: %ld\n"), ServiceName, ConcurrentLimit); +#endif + RegCloseKey(hKey2); + + } + + + if (Mode == 0) { + *PerSeatLicensing = TRUE; + *SessionLimit = 0; + } else { + *PerSeatLicensing = FALSE; + *SessionLimit = ConcurrentLimit; + } +#endif // #else // #ifdef SPECIAL_USER_LIMIT +} // RegistryInitValues + + +///////////////////////////////////////////////////////////////////////// +VOID +RegistryDisplayNameGet( + LPTSTR ServiceName, + LPTSTR DefaultName, + LPTSTR *pDisplayName + ) + +/*++ + +Routine Description: + + +Arguments: + + Service Name - + +Return Value: + + None. + +--*/ + +{ + HKEY hKey2 = NULL; + DWORD dwType, dwSize; + static TCHAR RegKeyText[512]; + static TCHAR DisplayName[512]; + LONG Status; + +#if DBG + if (TraceFlags & TRACE_FUNCTION_TRACE) + dprintf(TEXT("LLS TRACE: RegistryDisplayNameGet\n")); +#endif + + lstrcpy(DisplayName, DefaultName); + + // + // Create registry key-name we are looking for + // + lstrcpy(RegKeyText, TEXT("System\\CurrentControlSet\\Services\\LicenseInfo\\")); + lstrcat(RegKeyText, ServiceName); + + if ((Status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, RegKeyText, 0, KEY_READ, &hKey2)) == ERROR_SUCCESS) { + dwSize = sizeof(DisplayName); + Status = RegQueryValueEx(hKey2, TEXT("DisplayName"), NULL, &dwType, (LPBYTE) DisplayName, &dwSize); + +# if DBG + if ((TraceFlags & TRACE_REGISTRY) && (Status == ERROR_SUCCESS)) + dprintf(TEXT("Found Reg-Key for [%s] DisplayName: %s\n"), ServiceName, DisplayName); +# endif + RegCloseKey(hKey2); + + } + + *pDisplayName = LocalAlloc(LPTR, (lstrlen(DisplayName) + 1) * sizeof(TCHAR)); + if (*pDisplayName != NULL) + lstrcpy(*pDisplayName, DisplayName); + +} // RegistryDisplayNameGet + + +///////////////////////////////////////////////////////////////////////// +VOID +RegistryFamilyDisplayNameGet( + LPTSTR ServiceName, + LPTSTR DefaultName, + LPTSTR *pDisplayName + ) + +/*++ + +Routine Description: + + +Arguments: + + Service Name - + +Return Value: + + None. + +--*/ + +{ + HKEY hKey2 = NULL; + DWORD dwType, dwSize; + static TCHAR RegKeyText[512]; + static TCHAR DisplayName[MAX_PATH + 1]; + LONG Status; + +#if DBG + if (TraceFlags & TRACE_FUNCTION_TRACE) + dprintf(TEXT("LLS TRACE: RegistryFamilyDisplayNameGet\n")); +#endif + + lstrcpy(DisplayName, DefaultName); + + // + // Create registry key-name we are looking for + // + lstrcpy(RegKeyText, TEXT("System\\CurrentControlSet\\Services\\LicenseInfo\\")); + lstrcat(RegKeyText, ServiceName); + + if ((Status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, RegKeyText, 0, KEY_READ, &hKey2)) == ERROR_SUCCESS) { + dwSize = sizeof(DisplayName); + Status = RegQueryValueEx(hKey2, TEXT("FamilyDisplayName"), NULL, &dwType, (LPBYTE) DisplayName, &dwSize); + +# if DBG + if ((TraceFlags & TRACE_REGISTRY) && (Status == ERROR_SUCCESS)) + dprintf(TEXT("Found Reg-Key for [%s] FamilyDisplayName: %s\n"), ServiceName, DisplayName); +# endif + RegCloseKey(hKey2); + + } + + *pDisplayName = LocalAlloc(LPTR, (lstrlen(DisplayName) + 1) * sizeof(TCHAR)); + if (*pDisplayName != NULL) + lstrcpy(*pDisplayName, DisplayName); +} // RegistryFamilyDisplayNameGet + + +///////////////////////////////////////////////////////////////////////// +LPTSTR +ServiceFindInTable( + LPTSTR ServiceName, + LPTSTR Table[], + ULONG TableSize, + ULONG *TableIndex + ) + +/*++ + +Routine Description: + + Does search of table to find matching service name. + +Arguments: + + Service Name - + + Table - + + TableSize - + + TableIndex - + +Return Value: + + Pointer to found service or NULL if not found. + +--*/ + +{ + ULONG i = 0; + BOOL Found = FALSE; + +#if DBG + if (TraceFlags & TRACE_FUNCTION_TRACE) + dprintf(TEXT("LLS TRACE: ServiceFindInTable\n")); +#endif + while ((i < TableSize) && (!Found)) { + Found = !lstrcmpi(ServiceName, Table[i]); + i++; + } + + if (Found) { + i--; + *TableIndex = i; + return Table[i]; + } else + return NULL; + +} // ServiceFindInTable + + +///////////////////////////////////////////////////////////////////////// +VOID +RegistryInitService( + LPTSTR ServiceName, + BOOL *PerSeatLicensing, + ULONG *SessionLimit + ) + +/*++ + +Routine Description: + + Gets init values for a given service from the registry. If not found + then just returns default values. + +Arguments: + + ServiceName - + + PerSeatLicensing - + + SessionLimit - + +Return Value: + +--*/ + +{ + // + // These are the default values + // + ULONG TableEntry; + LPTSTR SvcName = NULL; + +#if DBG + if (TraceFlags & TRACE_FUNCTION_TRACE) + dprintf(TEXT("LLS TRACE: RegistryInitService\n")); +#endif + *PerSeatLicensing = FALSE; + *SessionLimit = 0; + + // + // Check if it is a file/print service - if so don't worry about rest + // of registry entries. + // + if (ServiceFindInTable(ServiceName, FilePrintTable, NumFilePrintEntries, &TableEntry)) { + return; + } + + // + // Not FilePrint - see if we need to map the name. + // + SvcName = ServiceFindInTable(ServiceName, NameMappingTable2, NUM_MAPPING_ENTRIES, &TableEntry); + + // if it wasn't found, use original ServiceName + if (SvcName == NULL) + SvcName = ServiceName; + + RegistryInitValues(SvcName, PerSeatLicensing, SessionLimit); + +#if DBG + if (TraceFlags & TRACE_REGISTRY) + if (*PerSeatLicensing) + dprintf(TEXT("LLS - Registry Init: PerSeat: Y Svc: %s\n"), SvcName); + else + dprintf(TEXT("LLS - Registry Init: PerSeat: N Svc: %s\n"), SvcName); +#endif + +} // RegistryInitService + + + +///////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////// + + + +///////////////////////////////////////////////////////////////////////// +VOID +LocalServiceListInit() + +/*++ + +Routine Description: + +Arguments: + + None. + +Return Value: + + None. + +--*/ + +{ + RtlInitializeResource(&LocalServiceListLock); + + // + // Now scan the registry and add all the services + // + LocalServiceListUpdate(); +} // LocalServiceListInit + + +///////////////////////////////////////////////////////////////////////// +int __cdecl LocalServiceListCompare(const void *arg1, const void *arg2) { + PLOCAL_SERVICE_RECORD Svc1, Svc2; + + Svc1 = (PLOCAL_SERVICE_RECORD) *((PLOCAL_SERVICE_RECORD *) arg1); + Svc2 = (PLOCAL_SERVICE_RECORD) *((PLOCAL_SERVICE_RECORD *) arg2); + + return lstrcmpi( Svc1->Name, Svc2->Name ); + +} // LocalServiceListCompare + + +///////////////////////////////////////////////////////////////////////// +PLOCAL_SERVICE_RECORD +LocalServiceListFind( + LPTSTR Name + ) + +/*++ + +Routine Description: + + Internal routine to actually do binary search on LocalServiceList, this + does not do any locking as we expect the wrapper routine to do this. + The search is a simple binary search. + +Arguments: + + ServiceName - + +Return Value: + + Pointer to found server table entry or NULL if not found. + +--*/ + +{ + LONG begin = 0; + LONG end = (LONG) LocalServiceListSize - 1; + LONG cur; + int match; + PLOCAL_SERVICE_RECORD Service; + +#if DBG + if (TraceFlags & TRACE_FUNCTION_TRACE) + dprintf(TEXT("LLS TRACE: LocalServiceListFind\n")); +#endif + + if ((LocalServiceListSize == 0) || (Name == NULL)) + return NULL; + + while (end >= begin) { + // go halfway in-between + cur = (begin + end) / 2; + Service = LocalServiceList[cur]; + + // compare the two result into match + match = lstrcmpi(Name, Service->Name); + + if (match < 0) + // move new begin + end = cur - 1; + else + begin = cur + 1; + + if (match == 0) + return Service; + } + + return NULL; + +} // LocalServiceListFind + + +///////////////////////////////////////////////////////////////////////// +PLOCAL_SERVICE_RECORD +LocalServiceListAdd( + LPTSTR Name, + LPTSTR DisplayName, + LPTSTR FamilyDisplayName, + DWORD ConcurrentLimit, + DWORD FlipAllow, + DWORD Mode, + DWORD HighMark + ) + +/*++ + +Routine Description: + + +Arguments: + + ServiceName - + +Return Value: + + Pointer to added service table entry, or NULL if failed. + +--*/ + +{ + LPTSTR NewName; + PLOCAL_SERVICE_RECORD Service; + +#if DBG + if (TraceFlags & TRACE_FUNCTION_TRACE) + dprintf(TEXT("LLS TRACE: LocalServiceListAdd\n")); +#endif + + if ((Name == NULL) || (*Name == TEXT('\0'))) { +#if DBG + dprintf(TEXT("Error LLS: LocalServiceListAdd Bad Parms\n")); +#endif + ASSERT(FALSE); + return NULL; + } + + // + // Try to find the name + // + Service = LocalServiceListFind(Name); + if (Service != NULL) { + Service->ConcurrentLimit = ConcurrentLimit; + Service->FlipAllow = FlipAllow; + Service->Mode = Mode; + return Service; + } + + // + // No record - so create a new one + // + if (LocalServiceList == NULL) { + LocalServiceList = (PLOCAL_SERVICE_RECORD *) LocalAlloc(LPTR, sizeof(PLOCAL_SERVICE_RECORD)); + } else { + LocalServiceList = (PLOCAL_SERVICE_RECORD *) LocalReAlloc(LocalServiceList, sizeof(PLOCAL_SERVICE_RECORD) * (LocalServiceListSize + 1), LHND); + } + + // + // Make sure we could allocate server table + // + if (LocalServiceList == NULL) { + ASSERT(FALSE); + LocalServiceListSize = 0; + return NULL; + } + + // + // Allocate space for Record. + // + Service = (PLOCAL_SERVICE_RECORD) LocalAlloc(LPTR, sizeof(LOCAL_SERVICE_RECORD)); + if (Service == NULL) { + ASSERT(FALSE); + return NULL; + } + + LocalServiceList[LocalServiceListSize] = Service; + + // + // Name + // + NewName = (LPTSTR) LocalAlloc(LPTR, (lstrlen(Name) + 1) * sizeof(TCHAR)); + if (NewName == NULL) { + ASSERT(FALSE); + LocalFree(Service); + return NULL; + } + + // now copy it over... + Service->Name = NewName; + lstrcpy(NewName, Name); + + // + // DisplayName + // + NewName = (LPTSTR) LocalAlloc(LPTR, (lstrlen(DisplayName) + 1) * sizeof(TCHAR)); + if (NewName == NULL) { + ASSERT(FALSE); + LocalFree(Service->Name); + LocalFree(Service); + return NULL; + } + + // now copy it over... + Service->DisplayName = NewName; + lstrcpy(NewName, DisplayName); + + // + // FamilyDisplayName + // + NewName = (LPTSTR) LocalAlloc(LPTR, (lstrlen(FamilyDisplayName) + 1) * sizeof(TCHAR)); + if (NewName == NULL) { + ASSERT(FALSE); + LocalFree(Service->Name); + LocalFree(Service->DisplayName); + LocalFree(Service); + return NULL; + } + + // now copy it over... + Service->FamilyDisplayName = NewName; + lstrcpy(NewName, FamilyDisplayName); + + // + // Initialize other stuff + // + Service->ConcurrentLimit = ConcurrentLimit; + Service->FlipAllow = FlipAllow; + Service->Mode = Mode; + Service->HighMark = HighMark; + + LocalServiceListSize++; + + // Have added the entry - now need to sort it in order of the service names + qsort((void *) LocalServiceList, (size_t) LocalServiceListSize, sizeof(PLOCAL_SERVICE_RECORD), LocalServiceListCompare); + + return Service; + +} // LocalServiceListAdd + + +///////////////////////////////////////////////////////////////////////// +VOID +LocalServiceListUpdate( ) + +/*++ + +Routine Description: + + Looks in registry for given service and sets values accordingly. + +Arguments: + +Return Value: + + None. + +--*/ + +{ + HKEY hKey2 = NULL; + HKEY hKey3 = NULL; + static TCHAR KeyName[MAX_PATH + 1]; + static TCHAR DisplayName[MAX_PATH + 1]; + static TCHAR FamilyDisplayName[MAX_PATH + 1]; + LONG EnumStatus; + LONG Status; + DWORD iSubKey = 0; + DWORD dwType, dwSize; + DWORD ConcurrentLimit, FlipAllow, Mode, HighMark; + +#if DBG + if (TraceFlags & TRACE_FUNCTION_TRACE) + dprintf(TEXT("LLS TRACE: LocalServiceListUpdate\n")); +#endif + + RtlAcquireResourceExclusive(&LocalServiceListLock, TRUE); + + EnumStatus = RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT("System\\CurrentControlSet\\Services\\LicenseInfo"), 0, KEY_READ, &hKey2); + + while (EnumStatus == ERROR_SUCCESS) { + EnumStatus = RegEnumKey(hKey2, iSubKey, KeyName, MAX_PATH + 1); + iSubKey++; + + if (EnumStatus == ERROR_SUCCESS) { + if ((Status = RegOpenKeyEx(hKey2, KeyName, 0, KEY_READ, &hKey3)) == ERROR_SUCCESS) { + dwSize = sizeof(DisplayName); + Status = RegQueryValueEx(hKey3, TEXT("DisplayName"), NULL, &dwType, (LPBYTE) DisplayName, &dwSize); + + dwSize = sizeof(FamilyDisplayName); + if (Status == ERROR_SUCCESS) { + Status = RegQueryValueEx(hKey3, TEXT("FamilyDisplayName"), NULL, &dwType, (LPBYTE) FamilyDisplayName, &dwSize); + + if (Status != ERROR_SUCCESS) { + lstrcpy(FamilyDisplayName, DisplayName); + Status = ERROR_SUCCESS; + } + } + + dwSize = sizeof(Mode); + if (Status == ERROR_SUCCESS) + Status = RegQueryValueEx(hKey3, TEXT("Mode"), NULL, &dwType, (LPBYTE) &Mode, &dwSize); + + dwSize = sizeof(FlipAllow); + if (Status == ERROR_SUCCESS) + Status = RegQueryValueEx(hKey3, TEXT("FlipAllow"), NULL, &dwType, (LPBYTE) &FlipAllow, &dwSize); + + dwSize = sizeof(ConcurrentLimit); + if (Status == ERROR_SUCCESS) + if (Mode == 0) + ConcurrentLimit = 0; + else + Status = RegQueryValueEx(hKey3, TEXT("ConcurrentLimit"), NULL, &dwType, (LPBYTE) &ConcurrentLimit, &dwSize); + + dwSize = sizeof(HighMark); + if (Status == ERROR_SUCCESS) { + Status = RegQueryValueEx(hKey3, TEXT("LocalKey"), NULL, &dwType, (LPBYTE) &HighMark, &dwSize); + if (Status != ERROR_SUCCESS) { + Status = ERROR_SUCCESS; + HighMark = 0; + } + } + + // + // If we read in everything then add to our table + // + if (Status == ERROR_SUCCESS) + LocalServiceListAdd(KeyName, DisplayName, FamilyDisplayName, ConcurrentLimit, FlipAllow, Mode, HighMark); + + RegCloseKey(hKey3); + } + } + } + + RegCloseKey(hKey2); + + RtlReleaseResource(&LocalServiceListLock); +} // LocalServiceListUpdate + + +///////////////////////////////////////////////////////////////////////// +VOID +LocalServiceListHighMarkSet( ) + +/*++ + +Routine Description: + +Arguments: + +Return Value: + + None. + +--*/ + +{ + HKEY hKey2 = NULL; + static TCHAR RegKeyText[512]; + LONG Status; + ULONG i, j; + PSERVICE_RECORD Service; + +#if DBG + if (TraceFlags & TRACE_FUNCTION_TRACE) + dprintf(TEXT("LLS TRACE: LocalServiceListHighMarkSet\n")); +#endif + + RtlAcquireResourceExclusive(&LocalServiceListLock, TRUE); + + for (i = 0; i < LocalServiceListSize; i++) { + RtlAcquireResourceShared(&ServiceListLock, TRUE); + j = 0; + Service = NULL; + + while ( (j < ServiceListSize) && (Service == NULL) ) { + if (!lstrcmpi(LocalServiceList[i]->DisplayName, ServiceList[j]->DisplayName) ) + Service = ServiceList[j]; + + j++; + } + + RtlReleaseResource(&ServiceListLock); + + if (Service != NULL) { + // + // Create registry key-name we are looking for + // + lstrcpy(RegKeyText, TEXT("System\\CurrentControlSet\\Services\\LicenseInfo\\")); + lstrcat(RegKeyText, LocalServiceList[i]->Name); + + Status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, RegKeyText, 0, KEY_WRITE, &hKey2); + + if (Status == ERROR_SUCCESS) + { + Status = RegSetValueEx(hKey2, TEXT("LocalKey"), 0, REG_DWORD, (LPBYTE) &Service->HighMark, sizeof(Service->HighMark)); + RegCloseKey( hKey2 ); + } + } + } + + RtlReleaseResource(&LocalServiceListLock); +} // LocalServiceListHighMarkSet + + +/////////////////////////////////////////////////////////////////////////////// +VOID +LocalServiceListConcurrentLimitSet( ) + +/*++ + +Routine Description: + + Write concurrent limit to the registry for all secure services. + + Modified from LocalServiceListHighMarkSet() implementation. + +Arguments: + + None. + +Return Value: + + None. + +--*/ + +{ + HKEY hKey2 = NULL; + TCHAR RegKeyText[512]; + LONG Status; + ULONG i, j; + +#if DBG + if (TraceFlags & TRACE_FUNCTION_TRACE) + dprintf(TEXT("LLS TRACE: LocalServiceListConcurrentLimitSet\n")); +#endif + + RtlAcquireResourceExclusive(&LocalServiceListLock, TRUE); + + for (i = 0; i < LocalServiceListSize; i++) + { + // + // Create registry key-name we are looking for + // + lstrcpy(RegKeyText, TEXT("System\\CurrentControlSet\\Services\\LicenseInfo\\")); + lstrcat(RegKeyText, LocalServiceList[i]->Name); + + Status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, RegKeyText, 0, KEY_READ, &hKey2); + + if (Status == ERROR_SUCCESS) + { + DWORD dwConcurrentLimit; + DWORD cbConcurrentLimit = sizeof( dwConcurrentLimit ); + DWORD dwType; + + // don't write unless we have to (to avoid triggering the registry monitor thread) + Status = RegQueryValueEx(hKey2, TEXT("ConcurrentLimit"), NULL, &dwType, (LPBYTE) &dwConcurrentLimit, &cbConcurrentLimit ); + + if ( ServiceIsSecure( LocalServiceList[i]->DisplayName ) ) + { + LocalServiceList[i]->ConcurrentLimit = LocalServiceList[i]->Mode ? ProductLicensesGet( LocalServiceList[i]->DisplayName, TRUE ) : 0; + + // secure product + if ( ( ERROR_SUCCESS != Status ) + || ( REG_DWORD != dwType ) + || ( dwConcurrentLimit != LocalServiceList[i]->ConcurrentLimit ) ) + { + RegCloseKey( hKey2 ); + Status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, RegKeyText, 0, KEY_WRITE, &hKey2); + + ASSERT( ERROR_SUCCESS == Status ); + if ( ERROR_SUCCESS == Status ) + { + Status = RegSetValueEx(hKey2, TEXT("ConcurrentLimit"), 0, REG_DWORD, (LPBYTE) &LocalServiceList[i]->ConcurrentLimit, sizeof( LocalServiceList[i]->ConcurrentLimit ) ); + } + } + } + + RegCloseKey( hKey2 ); + } + } + + RtlReleaseResource(&LocalServiceListLock); +} // LocalServiceListConcurrentLimitSet + + +/////////////////////////////////////////////////////////////////////////////// +BOOL ServiceIsSecure( LPTSTR ServiceName ) + +/*++ + +Routine Description: + + Determine whether a given service disallows 3.51 Honesty-style + license purchases. + +Arguments: + + ServiceName (LPTSTR) + Service to check. + +Return Value: + + TRUE if service requires secure certificate, + FALSE if it accepts 3.51 Honesty-style license purchases. + +--*/ + +{ + BOOL IsSecure = FALSE; + + if ( NULL != SecureServiceList ) + { + DWORD i; + + RtlEnterCriticalSection( &ConfigInfoLock ); + + for ( i=0; i < SecureServiceListSize; i++ ) + { + if ( !lstrcmpi( SecureServiceList[i], ServiceName ) ) + { + IsSecure = TRUE; + break; + } + } + + RtlLeaveCriticalSection( &ConfigInfoLock ); + } + + return IsSecure; +} + + +/////////////////////////////////////////////////////////////////////////////// +NTSTATUS ServiceSecuritySet( LPTSTR ServiceName ) + +/*++ + +Routine Description: + + Add a given service to the secure service list. + +Arguments: + + ServiceName (LPTSTR) + Service to add. + +Return Value: + + STATUS_SUCCESS or Win error or NTSTATUS error code. + +--*/ + +{ + NTSTATUS nt; + DWORD i; + BOOL bChangedValue = FALSE; + + RtlEnterCriticalSection( &ConfigInfoLock ); + + for ( i=0; i < SecureServiceListSize; i++ ) + { + if ( !lstrcmpi( SecureServiceList[i], ServiceName ) ) + { + // product already registered as secure + break; + } + } + + if ( i < SecureServiceListSize ) + { + // product already registered as secure + nt = STATUS_SUCCESS; + } + else + { + TCHAR * NewSecureServiceBuffer; + ULONG NewSecureServiceBufferSize; + + NewSecureServiceBufferSize = ( SecureServiceBufferSize ? SecureServiceBufferSize : sizeof( TCHAR ) ) + sizeof( TCHAR ) * ( 1 + lstrlen( ServiceName ) ); + NewSecureServiceBuffer = LocalAlloc( LPTR, NewSecureServiceBufferSize ); + + if ( NULL == NewSecureServiceBuffer ) + { + nt = STATUS_NO_MEMORY; + ASSERT( FALSE ); + } + else + { + if ( NULL != SecureServiceBuffer ) + { + // copy over current secure service strings + memcpy( NewSecureServiceBuffer, SecureServiceBuffer, SecureServiceBufferSize - sizeof( TCHAR ) ); + + // add new secure service (don't forget last string is followed by 2 nulls) + memcpy( (LPBYTE) NewSecureServiceBuffer + SecureServiceBufferSize - sizeof( TCHAR ), ServiceName, NewSecureServiceBufferSize - SecureServiceBufferSize - sizeof( TCHAR ) ); + } + else + { + // add new secure service (don't forget last string is followed by 2 nulls) + memcpy( NewSecureServiceBuffer, ServiceName, NewSecureServiceBufferSize - sizeof( TCHAR ) ); + } + + ASSERT( 0 == *( (LPBYTE) NewSecureServiceBuffer + NewSecureServiceBufferSize - 2 * sizeof( TCHAR ) ) ); + ASSERT( 0 == *( (LPBYTE) NewSecureServiceBuffer + NewSecureServiceBufferSize - sizeof( TCHAR ) ) ); + + // encrypt buffer + nt = EBlock( NewSecureServiceBuffer, NewSecureServiceBufferSize ); + ASSERT( STATUS_SUCCESS == nt ); + + if ( STATUS_SUCCESS == nt ) + { + HKEY hKeyParameters; + + // save new list to registry + nt = RegOpenKeyEx( HKEY_LOCAL_MACHINE, TEXT("System\\CurrentControlSet\\Services\\LicenseService\\Parameters"), 0, KEY_WRITE, &hKeyParameters ); + ASSERT( STATUS_SUCCESS == nt ); + + if ( STATUS_SUCCESS == nt ) + { + nt = RegSetValueEx( hKeyParameters, TEXT( "ProductData" ), 0, REG_BINARY, (LPBYTE) NewSecureServiceBuffer, NewSecureServiceBufferSize ); + ASSERT( STATUS_SUCCESS == nt ); + + if ( STATUS_SUCCESS == nt ) + { + bChangedValue = TRUE; + } + } + + RegCloseKey( hKeyParameters ); + } + + LocalFree( NewSecureServiceBuffer ); + } + } + + RtlLeaveCriticalSection( &ConfigInfoLock ); + + if ( ( STATUS_SUCCESS == nt ) && bChangedValue ) + { + // key updated, now update internal copy + ConfigInfoRegistryUpdate(); + } + + return nt; +} + + +/////////////////////////////////////////////////////////////////////////////// +NTSTATUS ProductSecurityPack( LPDWORD pcchProductSecurityStrings, WCHAR ** ppchProductSecurityStrings ) + +/*++ + +Routine Description: + + Pack the secure service list into a contiguous buffer for transmission. + + NOTE: If the routine succeeds, the caller must later MIDL_user_free() the + buffer at *ppchProductSecurityStrings. + +Arguments: + + pcchProductSecurityStrings (LPDWORD) + On return, holds the size (in characters) of the buffer pointed to + by *ppchProductSecurityStrings. + ppchProductSecurityStrings (WCHAR **) + On return, holds the address of the buffer allocated to hold the names + of the secure products. + +Return Value: + + STATUS_SUCCESS or STATUS_NO_MEMORY. + +--*/ + +{ + NTSTATUS nt; + + RtlEnterCriticalSection( &ConfigInfoLock ); + + *ppchProductSecurityStrings = MIDL_user_allocate( SecureServiceBufferSize ); + + if ( NULL == *ppchProductSecurityStrings ) + { + nt = STATUS_NO_MEMORY; + ASSERT( FALSE ); + } + else + { + memcpy( *ppchProductSecurityStrings, SecureServiceBuffer, SecureServiceBufferSize ); + *pcchProductSecurityStrings = SecureServiceBufferSize / sizeof( TCHAR ); + + nt = STATUS_SUCCESS; + } + + RtlLeaveCriticalSection( &ConfigInfoLock ); + + return nt; +} + + +/////////////////////////////////////////////////////////////////////////////// +NTSTATUS ProductSecurityUnpack( DWORD cchProductSecurityStrings, WCHAR * pchProductSecurityStrings ) + +/*++ + +Routine Description: + + Unpack a secure service list packed by ProductSecurityPack(). The products + contained in the pack are added to the current secure product list. + +Arguments: + + cchProductSecurityStrings (DWORD) + The size (in characters) of the buffer pointed to by pchProductSecurityStrings. + pchProductSecurityStrings (WCHAR *) + The address of the buffer allocated to hold the names of the secure products. + +Return Value: + + STATUS_SUCCESS. + +--*/ + +{ + DWORD i; + + for ( i=0; + ( i < cchProductSecurityStrings ) + && ( TEXT('\0') != pchProductSecurityStrings[i] ); + i += 1 + lstrlen( &pchProductSecurityStrings[i] ) ) + { + ServiceSecuritySet( &pchProductSecurityStrings[i] ); + } + + return STATUS_SUCCESS; +} + +#if DBG +/////////////////////////////////////////////////////////////////////////////// +void ProductSecurityListDebugDump() + +/*++ + +Routine Description: + + Dump contents of product security list to debug console. + +Arguments: + + None. + +Return Value: + + None. + +--*/ + +{ + if ( NULL == SecureServiceList ) + { + dprintf( TEXT( "No secure products.\n" ) ); + } + else + { + DWORD i; + + RtlEnterCriticalSection( &ConfigInfoLock ); + + for ( i=0; i < SecureServiceListSize; i++ ) + { + dprintf( TEXT( "(%3ld) %s\n" ), (long)i, SecureServiceList[i] ); + } + + RtlLeaveCriticalSection( &ConfigInfoLock ); + } +} +#endif diff --git a/private/net/svcdlls/lls/server/registry.h b/private/net/svcdlls/lls/server/registry.h new file mode 100644 index 000000000..445bd0730 --- /dev/null +++ b/private/net/svcdlls/lls/server/registry.h @@ -0,0 +1,83 @@ +/*++ + +Copyright (c) 1994 Microsoft Corporation + +Module Name: + + Registry.h + +Abstract: + + +Author: + + Arthur Hanson (arth) Dec 07, 1994 + +Environment: + +Revision History: + + Jeff Parham (jeffparh) 05-Dec-1995 + o Added secure service list. This list tracks the products that + require "secure" license certificates for all licenses; i.e., the + products that do not accept the 3.51 Honesty method of "enter the + number of licenses you purchased." + o Added routine to update the concurrent limit value in the registry + to accurately reflect the connection limit of secure products. + +--*/ + +#ifndef _LLS_REGISTRY_H +#define _LLS_REGISTRY_H + + +#ifdef __cplusplus +extern "C" { +#endif + + +typedef struct _LOCAL_SERVICE_RECORD { + LPTSTR Name; + LPTSTR DisplayName; + LPTSTR FamilyDisplayName; + DWORD ConcurrentLimit; + DWORD FlipAllow; + DWORD Mode; + ULONG HighMark; +} LOCAL_SERVICE_RECORD, *PLOCAL_SERVICE_RECORD; + +extern ULONG LocalServiceListSize; +extern PLOCAL_SERVICE_RECORD *LocalServiceList; +extern RTL_RESOURCE LocalServiceListLock; + + +VOID RegistryInit( ); +VOID RegistryStartMonitor( ); +VOID ConfigInfoRegistryInit( DWORD *pUseEnterprise, LPTSTR pEnterpriseServer, DWORD *pReplicationType, DWORD *pReplicationTime, DWORD *pLogLevel ); +VOID RegistryInitValues( LPTSTR ServiceName, BOOL *PerSeatLicensing, ULONG *SessionLimit ); +VOID RegistryDisplayNameGet( LPTSTR ServiceName, LPTSTR DefaultName, LPTSTR *pDisplayName ); +VOID RegistryFamilyDisplayNameGet( LPTSTR ServiceName, LPTSTR DefaultName, LPTSTR *pDisplayName ); +VOID RegistryInitService( LPTSTR ServiceName, BOOL *PerSeatLicensing, ULONG *SessionLimit ); +LPTSTR ServiceFindInTable( LPTSTR ServiceName, LPTSTR Table[], ULONG TableSize, ULONG *TableIndex ); + +VOID LocalServiceListInit(); +PLOCAL_SERVICE_RECORD LocalServiceListFind( LPTSTR Name ); +PLOCAL_SERVICE_RECORD LocalServiceListAdd( LPTSTR Name, LPTSTR DisplayName, LPTSTR FamilyDisplayName, DWORD ConcurrentLimit, DWORD FlipAllow, DWORD Mode, DWORD SessLimit ); +VOID LocalServiceListUpdate( ); +VOID LocalServiceListHighMarkSet( ); +VOID LocalServiceListConcurrentLimitSet( ); + +BOOL ServiceIsSecure( LPTSTR ServiceName ); +NTSTATUS ServiceSecuritySet( LPTSTR ServiceName ); +NTSTATUS ProductSecurityUnpack( DWORD cchProductSecurityStrings, WCHAR * pchProductSecurityStrings ); +NTSTATUS ProductSecurityPack( LPDWORD pcchProductSecurityStrings, WCHAR ** ppchProductSecurityStrings ); + +#if DBG +void ProductSecurityListDebugDump(); +#endif + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/private/net/svcdlls/lls/server/repl.c b/private/net/svcdlls/lls/server/repl.c new file mode 100644 index 000000000..725c25174 --- /dev/null +++ b/private/net/svcdlls/lls/server/repl.c @@ -0,0 +1,641 @@ +/*++ + +Copyright (c) 1995 Microsoft Corporation + +Module Name: + + repl.c + +Abstract: + +Author: + + Arthur Hanson (arth) 06-Jan-1995 + +Revision History: + + Jeff Parham (jeffparh) 05-Dec-1995 + o Added replication of certificate database and secure service list. + o Log failure to connect during replication only if the target server + is running a build in which license server should be available (i.e., + 1057 (3.51) or greater). If the target server does not support + license server, log a message to that effect only once. + +--*/ + +#include <nt.h> +#include <ntrtl.h> +#include <nturtl.h> +#include <windows.h> + +#include "debug.h" +#include "llsutil.h" +#include <llsapi.h> +#include "llssrv.h" +#include "mapping.h" +#include "msvctbl.h" +#include "svctbl.h" +#include "perseat.h" +#include "server.h" +#include "repl.h" +#include "llsrpc_s.h" +#include "pack.h" +#include "llsevent.h" +#include "certdb.h" +#include "registry.h" + +HANDLE ReplicationEvent; + +HANDLE LlsRPCHandle = NULL; +FARPROC pLlsReplConnect; +FARPROC pLlsReplClose; +FARPROC pLlsFreeMemory; + +FARPROC pLlsReplicationRequestW = NULL; +FARPROC pLlsReplicationServerAddW = NULL; +FARPROC pLlsReplicationServerServiceAddW = NULL; +FARPROC pLlsReplicationServiceAddW = NULL; +FARPROC pLlsReplicationUserAddW = NULL; + +PLLS_CAPABILITY_IS_SUPPORTED pLlsCapabilityIsSupported = NULL; +PLLS_REPLICATION_CERT_DB_ADD_W pLlsReplicationCertDbAddW = NULL; +PLLS_REPLICATION_PRODUCT_SECURITY_ADD_W pLlsReplicationProductSecurityAddW = NULL; +PLLS_REPLICATION_USER_ADD_EX_W pLlsReplicationUserAddExW = NULL; +PLLS_CONNECT_W pLlsConnectW = NULL; +PLLS_CLOSE pLlsClose = NULL; + +///////////////////////////////////////////////////////////////////////// +NTSTATUS +ReplicationInit ( ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + +--*/ + +{ + DWORD Ignore; + HANDLE Thread; + NTSTATUS Status; + DWORD Time; + +#if DBG + if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_REPLICATION)) + dprintf(TEXT("LLS TRACE: ReplicationInit\n")); +#endif + + // + // Open up our RPC DLL and init our function references. + // + LlsRPCHandle = LoadLibrary(TEXT("LLSRPC.DLL")); + ASSERT(LlsRPCHandle != NULL); + + if (LlsRPCHandle != NULL) { + pLlsReplConnect = GetProcAddress(LlsRPCHandle, ("LlsReplConnectW")); + pLlsReplClose = GetProcAddress(LlsRPCHandle, ("LlsReplClose")); + pLlsFreeMemory = GetProcAddress(LlsRPCHandle, ("LlsFreeMemory")); + pLlsReplicationRequestW = GetProcAddress(LlsRPCHandle, ("LlsReplicationRequestW")); + pLlsReplicationServerAddW = GetProcAddress(LlsRPCHandle, ("LlsReplicationServerAddW")); + pLlsReplicationServerServiceAddW = GetProcAddress(LlsRPCHandle, ("LlsReplicationServerServiceAddW")); + pLlsReplicationServiceAddW = GetProcAddress(LlsRPCHandle, ("LlsReplicationServiceAddW")); + pLlsReplicationUserAddW = GetProcAddress(LlsRPCHandle, ("LlsReplicationUserAddW")); + pLlsReplicationCertDbAddW = (PLLS_REPLICATION_CERT_DB_ADD_W) GetProcAddress(LlsRPCHandle, ("LlsReplicationCertDbAddW")); + pLlsReplicationProductSecurityAddW = (PLLS_REPLICATION_PRODUCT_SECURITY_ADD_W) GetProcAddress(LlsRPCHandle, ("LlsReplicationProductSecurityAddW")); + pLlsReplicationUserAddExW = (PLLS_REPLICATION_USER_ADD_EX_W) GetProcAddress(LlsRPCHandle, ("LlsReplicationUserAddExW")); + pLlsCapabilityIsSupported = (PLLS_CAPABILITY_IS_SUPPORTED) GetProcAddress(LlsRPCHandle, ("LlsCapabilityIsSupported")); + pLlsConnectW = (PLLS_CONNECT_W) GetProcAddress(LlsRPCHandle, ("LlsConnectW")); + pLlsClose = (PLLS_CLOSE) GetProcAddress(LlsRPCHandle, ("LlsClose")); + + ASSERT (pLlsReplConnect != NULL); + ASSERT (pLlsReplClose != NULL); + ASSERT (pLlsFreeMemory != NULL); + ASSERT (pLlsReplicationRequestW != NULL); + ASSERT (pLlsReplicationServerAddW != NULL); + ASSERT (pLlsReplicationServerServiceAddW != NULL); + ASSERT (pLlsReplicationServiceAddW != NULL); + ASSERT (pLlsReplicationUserAddW != NULL); + ASSERT (pLlsReplicationCertDbAddW != NULL); + ASSERT (pLlsReplicationProductSecurityAddW != NULL); + ASSERT (pLlsReplicationUserAddExW != NULL); + ASSERT (pLlsCapabilityIsSupported != NULL); + ASSERT (pLlsConnectW != NULL); + ASSERT (pLlsClose != NULL); + + if ((pLlsReplConnect != NULL) && (pLlsReplClose != NULL) && + (pLlsFreeMemory != NULL) && (pLlsReplicationRequestW != NULL) && + (pLlsReplicationServerAddW != NULL) && (pLlsReplicationServiceAddW != NULL) && + (pLlsReplicationServerServiceAddW != NULL) && (pLlsReplicationUserAddW != NULL) && + (pLlsReplicationCertDbAddW != NULL) && (pLlsReplicationProductSecurityAddW != NULL) && + (pLlsReplicationUserAddExW != NULL) && (pLlsCapabilityIsSupported != NULL) && + (pLlsConnectW != NULL) && (pLlsClose != NULL) + ) { + + // + // Create the Replication Management event + // + Status = NtCreateEvent( + &ReplicationEvent, + EVENT_QUERY_STATE | EVENT_MODIFY_STATE | SYNCHRONIZE, + NULL, + SynchronizationEvent, + FALSE + ); + + ASSERT(NT_SUCCESS(Status)); + + // + // Fire off the thread to watch for replication. + // + Thread = CreateThread( + NULL, + 0L, + (LPTHREAD_START_ROUTINE) ReplicationManager, + 0L, + 0L, + &Ignore + ); + + } + } + + return STATUS_SUCCESS; +} // ReplicationInit + + +///////////////////////////////////////////////////////////////////////// +NTSTATUS +ReplicationDo ( + LLS_HANDLE LlsHandle, + LLS_REPL_HANDLE ReplHandle, + DWORD LastReplicated + ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ + +{ + NTSTATUS Status; + PREPL_SERVICE_RECORD Services = NULL; + ULONG ServicesTotalRecords = 0; + PREPL_SERVER_RECORD Servers = NULL; + ULONG ServersTotalRecords = 0; + PREPL_SERVER_SERVICE_RECORD ServerServices = NULL; + ULONG ServerServicesTotalRecords = 0; + + REPL_CERTIFICATE_DB_0 CertificateDB; + REPL_PRODUCT_SECURITY_0 ProductSecurity; + + DWORD UserLevel = 0; + REPL_USER_RECORD_CONTAINER UserDB; + + +#if DBG + if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_REPLICATION)) + dprintf(TEXT("LLS TRACE: ReplicationDo\n")); +#endif + + // + // Pack all of our data into linear / self-relative buffers so we + // can send them over. + // + ZeroMemory( &UserDB, sizeof( UserDB ) ); + ZeroMemory( &CertificateDB, sizeof( CertificateDB ) ); + ZeroMemory( &ProductSecurity, sizeof( ProductSecurity ) ); + + if ( (*pLlsCapabilityIsSupported)( LlsHandle, LLS_CAPABILITY_REPLICATE_USERS_EX ) ) + { + UserLevel = 1; + Status = PackAll( LastReplicated, &ServicesTotalRecords, &Services, &ServersTotalRecords, &Servers, &ServerServicesTotalRecords, &ServerServices, 1, &UserDB.Level1.NumUsers, &UserDB.Level1.Users ); + if (Status != STATUS_SUCCESS) + goto ReplicationDoExit; + } + else + { + UserLevel = 0; + Status = PackAll( LastReplicated, &ServicesTotalRecords, &Services, &ServersTotalRecords, &Servers, &ServerServicesTotalRecords, &ServerServices, 0, &UserDB.Level0.NumUsers, &UserDB.Level0.Users ); + if (Status != STATUS_SUCCESS) + goto ReplicationDoExit; + } + + if ( (*pLlsCapabilityIsSupported)( LlsHandle, LLS_CAPABILITY_REPLICATE_CERT_DB ) ) + { + Status = CertDbPack( &CertificateDB.StringSize, &CertificateDB.Strings, &CertificateDB.HeaderContainer.Level0.NumHeaders, &CertificateDB.HeaderContainer.Level0.Headers, &CertificateDB.ClaimContainer.Level0.NumClaims, &CertificateDB.ClaimContainer.Level0.Claims ); + if (Status != STATUS_SUCCESS) + goto ReplicationDoExit; + } + + if ( (*pLlsCapabilityIsSupported)( LlsHandle, LLS_CAPABILITY_REPLICATE_PRODUCT_SECURITY ) ) + { + Status = ProductSecurityPack( &ProductSecurity.StringSize, &ProductSecurity.Strings ); + if (Status != STATUS_SUCCESS) + goto ReplicationDoExit; + } + + // + // Transmit... + // + + Status = (*pLlsReplicationServiceAddW) ( ReplHandle, ServicesTotalRecords, Services ); + if (Status != STATUS_SUCCESS) + goto ReplicationDoExit; + + Status = (*pLlsReplicationServerAddW) ( ReplHandle, ServersTotalRecords, Servers ); + if (Status != STATUS_SUCCESS) + goto ReplicationDoExit; + + Status = (*pLlsReplicationServerServiceAddW) ( ReplHandle, ServerServicesTotalRecords, ServerServices ); + if (Status != STATUS_SUCCESS) + goto ReplicationDoExit; + + if ( (*pLlsCapabilityIsSupported)( LlsHandle, LLS_CAPABILITY_REPLICATE_USERS_EX ) ) + { + Status = (*pLlsReplicationUserAddExW)( ReplHandle, UserLevel, &UserDB ); + if (Status != STATUS_SUCCESS) + goto ReplicationDoExit; + } + else + { + Status = (*pLlsReplicationUserAddW) ( ReplHandle, UserDB.Level0.NumUsers, UserDB.Level0.Users ); + if (Status != STATUS_SUCCESS) + goto ReplicationDoExit; + } + + if ( (*pLlsCapabilityIsSupported)( LlsHandle, LLS_CAPABILITY_REPLICATE_CERT_DB ) ) + { + Status = (*pLlsReplicationCertDbAddW)( ReplHandle, 0, &CertificateDB ); + if (Status != STATUS_SUCCESS) + goto ReplicationDoExit; + } + + if ( (*pLlsCapabilityIsSupported)( LlsHandle, LLS_CAPABILITY_REPLICATE_PRODUCT_SECURITY ) ) + { + Status = (*pLlsReplicationProductSecurityAddW)( ReplHandle, 0, &ProductSecurity ); + } + +ReplicationDoExit: + if (Status != STATUS_SUCCESS) { +#if DBG + dprintf(TEXT("LLS Replication ABORT: 0x%lX\n"), Status); +#endif + } + + if (Services != NULL) + MIDL_user_free(Services); + + if (Servers != NULL) + MIDL_user_free(Servers); + + if ( 0 == UserLevel ) + { + if (UserDB.Level0.Users != NULL) + MIDL_user_free(UserDB.Level0.Users); + } + else + { + if (UserDB.Level1.Users != NULL) + MIDL_user_free(UserDB.Level1.Users); + } + + if (CertificateDB.Strings != NULL) + MIDL_user_free(CertificateDB.Strings); + + if (CertificateDB.HeaderContainer.Level0.Headers != NULL) + MIDL_user_free(CertificateDB.HeaderContainer.Level0.Headers); + + if (CertificateDB.ClaimContainer.Level0.Claims != NULL) + MIDL_user_free(CertificateDB.ClaimContainer.Level0.Claims); + + if (ProductSecurity.Strings != NULL) + MIDL_user_free(ProductSecurity.Strings); + +#if DBG + if (TraceFlags & TRACE_REPLICATION) + dprintf(TEXT(" LLS Replication Finished\n")); +#endif + + return Status; + +} // ReplicationDo + + +///////////////////////////////////////////////////////////////////////// +VOID +ReplicationManager ( + IN PVOID ThreadParameter + ) + +/*++ + +Routine Description: + + +Arguments: + + ThreadParameter - Not used. + + +Return Value: + + This thread never exits. + +--*/ + +{ + BOOL DoReplication = FALSE; + NTSTATUS Status; + LLS_REPL_HANDLE ReplHandle = NULL; + LLS_HANDLE LlsHandle = NULL; + PLLS_CONNECT_INFO_0 pConnectInfo; + PREPL_REQUEST pReplInfo; + TCHAR ReplicateTo[MAX_COMPUTERNAME_LENGTH + 3]; + DWORD LastReplicated; + LPTSTR pReplicateTo = ReplicateTo; + TCHAR LastFailedConnectionDownlevelReplicateTo[MAX_COMPUTERNAME_LENGTH + 3] = TEXT(""); + +#if DBG + if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_REPLICATION)) + dprintf(TEXT("LLS TRACE: ReplicationManager\n")); +#endif + + // + // Loop forever waiting to be given the opportunity to serve the + // greater good. + // + for ( ; ; ) { + // + // Wait to be notified that there is work to be done + // + Status = NtWaitForSingleObject( ReplicationEvent, TRUE, NULL ); + + // + // So they said, go replicate my son... Yeah, but first we must ask + // the master for permission. + // + + // + // Construct our repl record + // + pReplInfo = MIDL_user_allocate(sizeof(REPL_REQUEST)); + ASSERT(pReplInfo != NULL); + if (pReplInfo != NULL) { + RtlEnterCriticalSection(&ConfigInfoLock); + lstrcpy(ReplicateTo, ConfigInfo.ReplicateTo); + + pReplInfo->EnterpriseServerDate = 0; + lstrcpy(pReplInfo->EnterpriseServer, ConfigInfo.EnterpriseServer); + pReplInfo->EnterpriseServerDate = ConfigInfo.EnterpriseServerDate; + + pReplInfo->LastReplicated = ConfigInfo.LastReplicatedSeconds; + pReplInfo->CurrentTime = LastUsedTime; + pReplInfo->NumberServices = 0; + pReplInfo->NumberUsers = 0; + + pReplInfo->ReplSize = MAX_REPL_SIZE; + + pReplInfo->Backoff = 0; + RtlLeaveCriticalSection(&ConfigInfoLock); + +#if DBG + if (TraceFlags & TRACE_REPLICATION) + dprintf(TEXT("LLS Starting Replication to: %s @ %s\n"), ReplicateTo, TimeToString(pReplInfo->CurrentTime)); +#endif + + Status = (*pLlsReplConnect) ( ReplicateTo, &ReplHandle, 0, (LPBYTE *) &pConnectInfo ); + + if ( STATUS_SUCCESS != Status ) + { +#if DBG + dprintf(TEXT("LLS Error: LlsReplConnect failed: 0x%lX\n"), Status); +#endif + ReplHandle = NULL; + } + else + { + Status = (*pLlsConnectW)( ReplicateTo, &LlsHandle ); + + if ( STATUS_SUCCESS != Status ) + { +#if DBG + dprintf(TEXT("LLS Error: LlsConnectW failed: 0x%lX\n"), Status); +#endif + LlsHandle = NULL; + } + } + + if (Status != STATUS_SUCCESS) + { + DWORD dwWinError; + DWORD dwBuildNumber; + + dwWinError = WinNtBuildNumberGet( ReplicateTo, &dwBuildNumber ); + + if ( ( ERROR_SUCCESS == dwWinError ) && ( dwBuildNumber < 1057L ) ) + { + // the ReplicateTo machine does not support the license service + if ( lstrcmpi( ReplicateTo, LastFailedConnectionDownlevelReplicateTo ) ) + { + lstrcpy( LastFailedConnectionDownlevelReplicateTo, ReplicateTo ); + + LogEvent( LLS_EVENT_REPL_DOWNLEVEL_TARGET, 1, &pReplicateTo, Status ); + } + } + else + { + // the ReplicateTo machine should be running the license service + *LastFailedConnectionDownlevelReplicateTo = TEXT( '\0' ); + + LogEvent( LLS_EVENT_REPL_NO_CONNECTION, 1, &pReplicateTo, Status ); + } + } + else + { + *LastFailedConnectionDownlevelReplicateTo = TEXT( '\0' ); + + Status = (*pLlsReplicationRequestW) ( ReplHandle, REPL_VERSION, pReplInfo ); + + if (Status != STATUS_SUCCESS) + { + LogEvent( LLS_EVENT_REPL_REQUEST_FAILED, 1, &pReplicateTo, Status ); + } + else + { + RtlEnterCriticalSection(&ConfigInfoLock); + lstrcpy(ConfigInfo.EnterpriseServer, pReplInfo->EnterpriseServer); + ConfigInfo.EnterpriseServerDate = pReplInfo->EnterpriseServerDate; + ConfigInfo.IsReplicating = TRUE; + LastReplicated = pReplInfo->LastReplicated; + RtlLeaveCriticalSection(&ConfigInfoLock); + + // + // And lo, thou may proceed... + // + if (pReplInfo->Backoff == 0) + { + if ( ConfigInfo.LogLevel ) + { + LogEvent( LLS_EVENT_REPL_START, 1, &pReplicateTo, ERROR_SUCCESS ); + } + + Status = ReplicationDo( LlsHandle, ReplHandle, LastReplicated ); + + if ( STATUS_SUCCESS != Status ) + { + LogEvent( LLS_EVENT_REPL_FAILED, 1, &pReplicateTo, Status ); + } + else if ( ConfigInfo.LogLevel ) + { + LogEvent( LLS_EVENT_REPL_END, 1, &pReplicateTo, ERROR_SUCCESS ); + } + + RtlEnterCriticalSection(&ConfigInfoLock); + + // + // Need to update when next we should replicate + // + ConfigInfo.LastReplicatedSeconds = DateSystemGet(); + GetLocalTime(&ConfigInfo.LastReplicated); + ReplicationTimeSet(); + } + else + { + LogEvent( LLS_EVENT_REPL_BACKOFF, 1, &pReplicateTo, ERROR_SUCCESS ); + RtlEnterCriticalSection(&ConfigInfoLock); + } + + ConfigInfo.IsReplicating = FALSE; + RtlLeaveCriticalSection(&ConfigInfoLock); + } + } + + // + // Disconnect from Master Server + // + if ( NULL != LlsHandle ) + { + (*pLlsClose)( LlsHandle ); + LlsHandle = NULL; + } + + if ( NULL != ReplHandle ) + { + Status = (*pLlsReplClose) ( &ReplHandle ); + try + { + RpcSmDestroyClientContext( &ReplHandle ); + } + except (TRUE) + { + Status = I_RpcMapWin32Status(RpcExceptionCode()); +#if DBG + dprintf(TEXT("ERROR LLSSRV.EXE (Repl): RPC Exception: 0x%lX\n"), Status); +#endif + } + + ReplHandle = NULL; + } + + MIDL_user_free( pReplInfo ); + } + } + +} // ReplicationManager + + +///////////////////////////////////////////////////////////////////////// +VOID +ReplicationTimeSet ( ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + +--*/ + +{ + DWORD CurrTime, ReplTime, Time; + +#if DBG + if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_REPLICATION)) + dprintf(TEXT("LLS TRACE: ReplicationTimeSet\n")); +#endif + + ReplTime = Time = 0; + + // + // Figure out what new time to set it to + // + if (!ConfigInfo.Replicate) + return; + + // + // If REPLICATE_DELTA it is easy as we just take the delta and apply it to + // the last replication time. Otherwise we have to convert the time from + // midnight. + // + + // + // Figure out how long since we last replicated + // + ReplTime = ConfigInfo.ReplicationTime; + + if (ConfigInfo.ReplicationType == REPLICATE_DELTA) { + Time = DateSystemGet() - ConfigInfo.LastReplicatedSeconds; + + // + // If we have already gone past when we should replicate then schedule + // one real soon now (10 minutes). + // + if (Time > ReplTime) + Time = 10 * 60; + else + Time = ReplTime - Time; + + Time += DateLocalGet(); + } else { + // + // Need to adjust time to midnight - do this by MOD of seconds + // per day. + // + CurrTime = DateLocalGet(); + Time = CurrTime - ((CurrTime / (60 * 60 * 24)) * (60 * 60 * 24)); + CurrTime = CurrTime - Time; + + // + // Time = seconds past midnight. + // CurrTime = Todays @ 12:00 AM + // Figure out if we are past the replication time, if so schedule it + // for tomorrow, else today. + // + if (Time > ReplTime) + Time = CurrTime + (60 * 60 * 24) + ReplTime; + else + Time = CurrTime + ReplTime; + + } + + ConfigInfo.NextReplication = Time; + +} // ReplicationTimeSet diff --git a/private/net/svcdlls/lls/server/repl.h b/private/net/svcdlls/lls/server/repl.h new file mode 100644 index 000000000..4cfa4f37f --- /dev/null +++ b/private/net/svcdlls/lls/server/repl.h @@ -0,0 +1,47 @@ +/*++ + +Copyright (c) 1994 Microsoft Corporation + +Module Name: + + Repl.h + +Abstract: + + +Author: + + Arthur Hanson (arth) Dec 07, 1994 + +Environment: + +Revision History: + +--*/ + +#ifndef _LLS_REPL_H +#define _LLS_REPL_H + + +#ifdef __cplusplus +extern "C" { +#endif + +// +// Maximum number of records we will send over to the server at once. +// +#define MAX_REPL_SIZE 25 +#define REPL_VERSION 0x0102 + +extern HANDLE ReplicationEvent; + + +NTSTATUS ReplicationInit(); +VOID ReplicationManager ( IN PVOID ThreadParameter ); +VOID ReplicationTimeSet ( ); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/private/net/svcdlls/lls/server/rpc.c b/private/net/svcdlls/lls/server/rpc.c new file mode 100644 index 000000000..dc3d8eb14 --- /dev/null +++ b/private/net/svcdlls/lls/server/rpc.c @@ -0,0 +1,6121 @@ +/*++ + +Copyright (c) 1995 Microsoft Corporation + +Module Name: + + rpc.c + +Abstract: + + +Author: + + Arthur Hanson (arth) 06-Jan-1995 + +Revision History: + + Jeff Parham (jeffparh) 05-Dec-1995 + o Added replication of certificate database and secure service list. + o Added Llsr API to support secure certificates. + o Added LLS_LICENSE_INFO_1 support to LlsrLicenseEnumW() and + LlsrLicenseAddW(). + o Added LLS_PRODUCT_LICENSE_INFO_1 support to LlsrProductLicenseEnumW(). + o Added save of all data files after receiving replicated data. + +--*/ + +#include <nt.h> +#include <ntrtl.h> +#include <nturtl.h> +#include <windows.h> + +#include "llsapi.h" +#include "debug.h" +#include "llssrv.h" +#include "mapping.h" +#include "msvctbl.h" +#include "svctbl.h" +#include "perseat.h" +#include "purchase.h" +#include "server.h" + +#include "llsrpc_s.h" +#include "lsapi_s.h" +#include "llsdbg_s.h" +#include "repl.h" +#include "pack.h" +#include "registry.h" +#include "certdb.h" + + +// +// BUGBUG: maxpreflength does not currently work for the enum calls as it +// doesn't take into account the string fields in the records. +// + + +typedef struct { + TCHAR Name[MAX_COMPUTERNAME_LENGTH + 1]; +} CLIENT_CONTEXT_TYPE, *PCLIENT_CONTEXT_TYPE; + +typedef struct { + TCHAR Name[MAX_COMPUTERNAME_LENGTH + 1]; + DWORD ReplicationStart; + + BOOL Active; + BOOL Replicated; + + BOOL ServicesSent; + ULONG ServiceTableSize; + PREPL_SERVICE_RECORD Services; + + BOOL ServersSent; + ULONG ServerTableSize; + PREPL_SERVER_RECORD Servers; + + BOOL ServerServicesSent; + ULONG ServerServiceTableSize; + PREPL_SERVER_SERVICE_RECORD ServerServices; + + BOOL UsersSent; + ULONG UserLevel; + ULONG UserTableSize; + LPVOID Users; + + BOOL CertDbSent; + ULONG CertDbProductStringSize; + WCHAR * CertDbProductStrings; + ULONG CertDbNumHeaders; + PREPL_CERT_DB_CERTIFICATE_HEADER_0 CertDbHeaders; + ULONG CertDbNumClaims; + PREPL_CERT_DB_CERTIFICATE_CLAIM_0 CertDbClaims; + + BOOL ProductSecuritySent; + ULONG ProductSecurityStringSize; + WCHAR * ProductSecurityStrings; + +} REPL_CONTEXT_TYPE, *PREPL_CONTEXT_TYPE; + + +///////////////////////////////////////////////////////////////////////// +NTSTATUS +LLSRpcListen ( + IN PVOID ThreadParameter + ) + +/*++ + +Routine Description: + +Arguments: + + ThreadParameter - Indicates how many active threads there currently + are. + +Return Value: + + None. + +--*/ + +{ + RPC_STATUS Status; + + Status = RpcServerListen(1, 40, 0); + if (Status) { +#if DBG + dprintf(TEXT("RpcServerListen Failed (0x%lx)\n"), Status); +#endif + } + + return Status; + +} // LLSRpcListen + + +///////////////////////////////////////////////////////////////////////// +VOID +LLSRpcInit() + +/*++ + +Routine Description: + + +Arguments: + +Return Value: + + None. + +--*/ + +{ + RPC_STATUS Status; + DWORD Ignore; + PSECURITY_DESCRIPTOR pSD; + + // + // Initialize a security descriptor. + // + pSD = (PSECURITY_DESCRIPTOR) LocalAlloc(LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH); + if (pSD == NULL) { +#if DBG + dprintf(TEXT("LLS Error: RPC Security Descriptor Alloc Failed\n")); +#endif + ASSERT(FALSE); + return; + } + + if (!InitializeSecurityDescriptor(pSD, SECURITY_DESCRIPTOR_REVISION)) { +#if DBG + dprintf(TEXT("LLS Error: RPC Init Security Descriptor Failed\n")); +#endif + if(pSD != NULL) + LocalFree((HLOCAL) pSD); + + ASSERT(FALSE); + return; + } + + // + // Add a NULL disc. ACL to the security descriptor. + // + if (!SetSecurityDescriptorDacl(pSD, TRUE, (PACL) NULL, FALSE)) { +#if DBG + dprintf(TEXT("LLS Error: RPC SetSecurityDescriptorDacl\n")); +#endif + if(pSD != NULL) + LocalFree((HLOCAL) pSD); + + ASSERT(FALSE); + return; + } + + // + // Setup for LPC calls.. + // + Status = RpcServerUseProtseqEp(TEXT("ncalrpc"), RPC_C_PROTSEQ_MAX_REQS_DEFAULT, TEXT(LLS_LPC_ENDPOINT), pSD); + if (Status) { +#if DBG + dprintf(TEXT("RpcServerUseProtseq ncalrpc Failed (0x%lx)\n"), Status); +#endif + if(pSD != NULL) + LocalFree((HLOCAL) pSD); + + ASSERT(FALSE); + return; + } + + // Named pipes as well + Status = RpcServerUseProtseqEp(TEXT("ncacn_np"), RPC_C_PROTSEQ_MAX_REQS_DEFAULT, TEXT(LLS_NP_ENDPOINT), pSD); + if (Status) { +#if DBG + dprintf(TEXT("RpcServerUseProtseq ncacn_np Failed (0x%lx)\n"), Status); +#endif + if(pSD != NULL) + LocalFree((HLOCAL) pSD); + + ASSERT(FALSE); + return; + } + + if(pSD != NULL) + LocalFree((HLOCAL) pSD); + + // register the interface for the UI RPC's + Status = RpcServerRegisterIf(llsrpc_ServerIfHandle, NULL, NULL); + if (Status) { +#if DBG + dprintf(TEXT("RpcServerRegisterIf Failed (0x%lx)\n"), Status); +#endif + return; + } + + // Now the interface for the Licensing RPC's + Status = RpcServerRegisterIf(lsapirpc_ServerIfHandle, NULL, NULL); + if (Status) { +#if DBG + dprintf(TEXT("RpcServerRegisterIf2 Failed (0x%lx)\n"), Status); +#endif + return; + } + +#if DBG + // + // ... and if DEBUG then the debugging interface + // + Status = RpcServerRegisterIf(llsdbgrpc_ServerIfHandle, NULL, NULL); + if (Status) { +#if DBG + dprintf(TEXT("RpcServerRegisterIf (debug) Failed (0x%lx)\n"), Status); +#endif + return; + } +#endif + + // + // Create thread to listen for requests. + // + CreateThread( + NULL, + 0L, + (LPTHREAD_START_ROUTINE) LLSRpcListen, + 0L, + 0L, + &Ignore + ); + +} // LLSRpcInit + + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +///////////////////////////////////////////////////////////////////////// +VOID __RPC_USER LLS_HANDLE_rundown( + LLS_HANDLE Handle + ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ + +{ + CLIENT_CONTEXT_TYPE *pClient; + +#if DBG + if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC)) + dprintf(TEXT("LLS TRACE: LLS_HANDLE_rundown\n")); +#endif + + pClient = (CLIENT_CONTEXT_TYPE *) Handle; + + MIDL_user_free(pClient); + +} // LLS_HANDLE_rundown + + +///////////////////////////////////////////////////////////////////////// +NTSTATUS LlsrConnect( + PLLS_HANDLE Handle, + LPTSTR Name + ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ + +{ + CLIENT_CONTEXT_TYPE *pClient; + +#if DBG + if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC)) + dprintf(TEXT("LLS TRACE: LlsConnect: %s\n"), Name); +#endif + + pClient = (CLIENT_CONTEXT_TYPE *) midl_user_allocate(sizeof(CLIENT_CONTEXT_TYPE)); + + if (Name != NULL) + lstrcpy(pClient->Name, Name); + else + lstrcpy(pClient->Name, TEXT("")); + + *Handle = pClient; + + return STATUS_SUCCESS; +} // LlsrConnect + + +///////////////////////////////////////////////////////////////////////// +NTSTATUS LlsrClose( + LLS_HANDLE Handle + ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ + +{ + CLIENT_CONTEXT_TYPE *pClient; + +#if DBG + if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC)) + dprintf(TEXT("LLS TRACE: LlsClose\n")); +#endif + + pClient = (CLIENT_CONTEXT_TYPE *) Handle; + + return STATUS_SUCCESS; +} // LlsrClose + + +///////////////////////////////////////////////////////////////////////// +NTSTATUS LlsrLicenseEnumW( + LLS_HANDLE Handle, + PLLS_LICENSE_ENUM_STRUCTW pLicenseInfo, + DWORD pPrefMaxLen, + LPDWORD pTotalEntries, + LPDWORD pResumeHandle + ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ + +{ + NTSTATUS Status = STATUS_SUCCESS; + DWORD Level; + PVOID BufPtr = NULL; + ULONG BufSize = 0; + ULONG EntriesRead = 0; + ULONG TotalEntries = 0; + ULONG i = 0; + ULONG j = 0; + DWORD RecordSize; + +#if DBG + if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC)) + dprintf(TEXT("LLS TRACE: LlsLicenseEnumW\n")); +#endif + + Level = pLicenseInfo->Level; + + if ( 0 == Level ) + { + RecordSize = sizeof( LLS_LICENSE_INFO_0W ); + } + else if ( 1 == Level ) + { + RecordSize = sizeof( LLS_LICENSE_INFO_1W ); + } + else + { + return STATUS_INVALID_LEVEL; + } + // + // Need to scan list so get read access. + // + RtlAcquireResourceShared(&LicenseListLock, TRUE); + + // + // Calculate how many records will fit into PrefMaxLen buffer. + // + i = *pResumeHandle; + while ( ( i < PurchaseListSize ) && ( BufSize < pPrefMaxLen ) ) + { + if ( ( Level > 0 ) + || ( PurchaseList[i].AllowedModes & LLS_LICENSE_MODE_ALLOW_PER_SEAT ) ) + { + BufSize += RecordSize; + EntriesRead++; + } + + i++; + } + + TotalEntries = EntriesRead; + + // + // If we overflowed the buffer then back up one record. + // + if (BufSize > pPrefMaxLen) + { + BufSize -= RecordSize; + EntriesRead--; + } + + // + // Now walk to the end of the list to see how many more records are still + // available. + // + while ( i < PurchaseListSize ) + { + if ( ( Level > 0 ) + || ( PurchaseList[i].AllowedModes & LLS_LICENSE_MODE_ALLOW_PER_SEAT ) ) + { + TotalEntries++; + } + + i++; + } + + if (TotalEntries > EntriesRead) + Status = STATUS_MORE_ENTRIES; + + // + // Reset Enum to correct place. + // + i = *pResumeHandle; + + // + // We now know how many records will fit into the buffer, so allocate space + // and fix up pointers so we can copy the information. + // + BufPtr = MIDL_user_allocate(BufSize); + if (BufPtr == NULL) { + Status = STATUS_NO_MEMORY; + goto LlsrLicenseEnumWExit; + } + + RtlZeroMemory((PVOID) BufPtr, BufSize); + + // + // Buffers are all setup, so loop through records and copy the data. + // + while ((j < EntriesRead) && (i < PurchaseListSize)) + { + if ( ( Level > 0 ) + || ( PurchaseList[i].AllowedModes & LLS_LICENSE_MODE_ALLOW_PER_SEAT ) ) + { + if ( 0 == Level ) + { + ((PLLS_LICENSE_INFO_0W) BufPtr)[j].Product = PurchaseList[i].Service->ServiceName; + ((PLLS_LICENSE_INFO_0W) BufPtr)[j].Quantity = PurchaseList[i].NumberLicenses; + ((PLLS_LICENSE_INFO_0W) BufPtr)[j].Date = PurchaseList[i].Date; + ((PLLS_LICENSE_INFO_0W) BufPtr)[j].Admin = PurchaseList[i].Admin; + ((PLLS_LICENSE_INFO_0W) BufPtr)[j].Comment = PurchaseList[i].Comment; + } + else + { + ((PLLS_LICENSE_INFO_1W) BufPtr)[j].Product = ( PurchaseList[i].AllowedModes & LLS_LICENSE_MODE_ALLOW_PER_SEAT ) + ? PurchaseList[i].Service->ServiceName + : PurchaseList[i].PerServerService->ServiceName; + ((PLLS_LICENSE_INFO_1W) BufPtr)[j].Vendor = PurchaseList[i].Vendor; + ((PLLS_LICENSE_INFO_1W) BufPtr)[j].Quantity = PurchaseList[i].NumberLicenses; + ((PLLS_LICENSE_INFO_1W) BufPtr)[j].MaxQuantity = PurchaseList[i].MaxQuantity; + ((PLLS_LICENSE_INFO_1W) BufPtr)[j].Date = PurchaseList[i].Date; + ((PLLS_LICENSE_INFO_1W) BufPtr)[j].Admin = PurchaseList[i].Admin; + ((PLLS_LICENSE_INFO_1W) BufPtr)[j].Comment = PurchaseList[i].Comment; + ((PLLS_LICENSE_INFO_1W) BufPtr)[j].AllowedModes = PurchaseList[i].AllowedModes; + ((PLLS_LICENSE_INFO_1W) BufPtr)[j].CertificateID = PurchaseList[i].CertificateID; + ((PLLS_LICENSE_INFO_1W) BufPtr)[j].Source = PurchaseList[i].Source; + ((PLLS_LICENSE_INFO_1W) BufPtr)[j].ExpirationDate = PurchaseList[i].ExpirationDate; + memcpy( ((PLLS_LICENSE_INFO_1W) BufPtr)[j].Secrets, PurchaseList[i].Secrets, LLS_NUM_SECRETS * sizeof( *PurchaseList[i].Secrets ) ); + } + + j++; + } + + i++; + } + +LlsrLicenseEnumWExit: +#if DBG + if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC)) + dprintf(TEXT(" TotalEntries: %lu EntriesRead: %lu ResumeHandle: 0x%lX\n"), TotalEntries, EntriesRead, i); +#endif + *pTotalEntries = TotalEntries; + + *pResumeHandle = (ULONG) i; + + if ( 0 == Level ) + { + pLicenseInfo->LlsLicenseInfo.Level0->EntriesRead = EntriesRead; + pLicenseInfo->LlsLicenseInfo.Level0->Buffer = (PLLS_LICENSE_INFO_0W) BufPtr; + } + else + { + pLicenseInfo->LlsLicenseInfo.Level1->EntriesRead = EntriesRead; + pLicenseInfo->LlsLicenseInfo.Level1->Buffer = (PLLS_LICENSE_INFO_1W) BufPtr; + } + + RtlReleaseResource(&LicenseListLock); + return Status; +} // LlsrLicenseEnumW + + +///////////////////////////////////////////////////////////////////////// +NTSTATUS LlsrLicenseEnumA( + LLS_HANDLE Handle, + PLLS_LICENSE_ENUM_STRUCTA LicenseInfo, + DWORD PrefMaxLen, + LPDWORD TotalEntries, + LPDWORD ResumeHandle + ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ + +{ +#if DBG + if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC)) + dprintf(TEXT("LLS TRACE: LlsLicenseEnumA\n")); +#endif + + return STATUS_NOT_SUPPORTED; +} // LlsrLicenseEnumA + + +///////////////////////////////////////////////////////////////////////// +NTSTATUS LlsrLicenseAddW( + LLS_HANDLE Handle, + DWORD Level, + PLLS_LICENSE_INFOW BufPtr + ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ + +{ + NTSTATUS Status; + +#if DBG + if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC)) + dprintf(TEXT("LLS TRACE: LlsLicenseAddW\n")); +#endif + + if ( 0 == Level ) + { + if ( ( NULL == BufPtr ) + || ( NULL == BufPtr->LicenseInfo0.Product ) + || ( NULL == BufPtr->LicenseInfo0.Admin ) + || ( NULL == BufPtr->LicenseInfo0.Comment ) ) + { + Status = STATUS_INVALID_PARAMETER; + } + else + { + Status = LicenseAdd( BufPtr->LicenseInfo0.Product, + TEXT("Microsoft"), + BufPtr->LicenseInfo0.Quantity, + 0, + BufPtr->LicenseInfo0.Admin, + BufPtr->LicenseInfo0.Comment, + 0, + LLS_LICENSE_MODE_ALLOW_PER_SEAT, + 0, + TEXT("None"), + 0, + NULL ); + } + } + else if ( 1 == Level ) + { + if ( ( NULL == BufPtr ) + || ( NULL == BufPtr->LicenseInfo1.Product ) + || ( NULL == BufPtr->LicenseInfo1.Admin ) + || ( NULL == BufPtr->LicenseInfo1.Comment ) + || ( 0 == BufPtr->LicenseInfo1.Quantity ) + || ( 0 == ( BufPtr->LicenseInfo1.AllowedModes + & ( LLS_LICENSE_MODE_ALLOW_PER_SERVER + | LLS_LICENSE_MODE_ALLOW_PER_SEAT ) ) ) ) + { + Status = STATUS_INVALID_PARAMETER; + } + else + { + // check to see if this certificate is already maxed out in the enterprise + BOOL bIsMaster = TRUE; + BOOL bMayInstall = TRUE; + HINSTANCE hDll = NULL; + PLLS_CONNECT_ENTERPRISE_W pLlsConnectEnterpriseW = NULL; + PLLS_CLOSE pLlsClose = NULL; + PLLS_CAPABILITY_IS_SUPPORTED pLlsCapabilityIsSupported = NULL; + PLLS_CERTIFICATE_CLAIM_ADD_CHECK_W pLlsCertificateClaimAddCheckW = NULL; + PLLS_CERTIFICATE_CLAIM_ADD_W pLlsCertificateClaimAddW = NULL; + PLLS_FREE_MEMORY pLlsFreeMemory = NULL; + LLS_HANDLE hEnterpriseLls = NULL; + TCHAR szComputerName[ 1 + MAX_COMPUTERNAME_LENGTH ]; + + RtlEnterCriticalSection( &ConfigInfoLock ); + bIsMaster = ConfigInfo.IsMaster; + lstrcpy( szComputerName, ConfigInfo.ComputerName ); + RtlLeaveCriticalSection( &ConfigInfoLock ); + + if( !bIsMaster && ( 0 != BufPtr->LicenseInfo1.CertificateID ) ) + { + // ask enterprise server if we can install this certfificate + hDll = LoadLibraryA( "LLSRPC.DLL" ); + + if ( NULL == hDll ) + { + // LLSRPC.DLL should be available! + ASSERT( FALSE ); + } + else + { + pLlsConnectEnterpriseW = (PLLS_CONNECT_ENTERPRISE_W ) GetProcAddress( hDll, "LlsConnectEnterpriseW" ); + pLlsClose = (PLLS_CLOSE ) GetProcAddress( hDll, "LlsClose" ); + pLlsCapabilityIsSupported = (PLLS_CAPABILITY_IS_SUPPORTED ) GetProcAddress( hDll, "LlsCapabilityIsSupported" ); + pLlsCertificateClaimAddCheckW = (PLLS_CERTIFICATE_CLAIM_ADD_CHECK_W ) GetProcAddress( hDll, "LlsCertificateClaimAddCheckW" ); + pLlsCertificateClaimAddW = (PLLS_CERTIFICATE_CLAIM_ADD_W ) GetProcAddress( hDll, "LlsCertificateClaimAddW" ); + pLlsFreeMemory = (PLLS_FREE_MEMORY ) GetProcAddress( hDll, "LlsFreeMemory" ); + + if ( ( NULL == pLlsConnectEnterpriseW ) + || ( NULL == pLlsClose ) + || ( NULL == pLlsCapabilityIsSupported ) + || ( NULL == pLlsCertificateClaimAddCheckW ) + || ( NULL == pLlsCertificateClaimAddW ) + || ( NULL == pLlsFreeMemory ) ) + { + // All of these functions should be exported! + ASSERT( FALSE ); + } + else + { + PLLS_CONNECT_INFO_0 pConnectInfo; + + Status = (*pLlsConnectEnterpriseW)( NULL, &hEnterpriseLls, 0, (LPBYTE *)&pConnectInfo ); + + if ( STATUS_SUCCESS == Status ) + { + (*pLlsFreeMemory)( pConnectInfo ); + + if ( (*pLlsCapabilityIsSupported)( hEnterpriseLls, LLS_CAPABILITY_SECURE_CERTIFICATES ) ) + { + Status = (*pLlsCertificateClaimAddCheckW)( hEnterpriseLls, Level, (LPBYTE) BufPtr, &bMayInstall ); + + if ( STATUS_SUCCESS != Status ) + { + bMayInstall = TRUE; + } + } + } + } + } + } + + if ( !bMayInstall ) + { + // denied! + Status = STATUS_ALREADY_COMMITTED; + } + else + { + // approved! (or an error occurred trying to get approval...) + Status = LicenseAdd( BufPtr->LicenseInfo1.Product, + BufPtr->LicenseInfo1.Vendor, + BufPtr->LicenseInfo1.Quantity, + BufPtr->LicenseInfo1.MaxQuantity, + BufPtr->LicenseInfo1.Admin, + BufPtr->LicenseInfo1.Comment, + 0, + BufPtr->LicenseInfo1.AllowedModes, + BufPtr->LicenseInfo1.CertificateID, + BufPtr->LicenseInfo1.Source, + BufPtr->LicenseInfo1.ExpirationDate, + BufPtr->LicenseInfo1.Secrets ); + + if ( ( STATUS_SUCCESS == Status ) + && ( NULL != hEnterpriseLls ) + && ( (*pLlsCapabilityIsSupported)( hEnterpriseLls, LLS_CAPABILITY_SECURE_CERTIFICATES ) ) ) + { + // certificate successfully installed on this machine; register it + (*pLlsCertificateClaimAddW)( hEnterpriseLls, szComputerName, Level, (LPBYTE) BufPtr ); + } + } + + if ( NULL != hEnterpriseLls ) + { + (*pLlsClose)( hEnterpriseLls ); + } + + if ( NULL != hDll ) + { + FreeLibrary( hDll ); + } + } + } + else + { + Status = STATUS_INVALID_LEVEL; + } + + + if ( STATUS_SUCCESS == Status ) + { + Status = LicenseListSave(); + } + + return Status; +} // LlsrLicenseAddW + + +///////////////////////////////////////////////////////////////////////// +NTSTATUS LlsrLicenseAddA( + LLS_HANDLE Handle, + DWORD Level, + PLLS_LICENSE_INFOA BufPtr + ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ + +{ +#if DBG + if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC)) + dprintf(TEXT("LLS TRACE: LlsLicenseAddA\n")); +#endif + + return STATUS_NOT_SUPPORTED; +} // LlsrLicenseAddA + + +///////////////////////////////////////////////////////////////////////// +NTSTATUS LlsrProductEnumW( + LLS_HANDLE Handle, + PLLS_PRODUCT_ENUM_STRUCTW pProductInfo, + DWORD pPrefMaxLen, + LPDWORD pTotalEntries, + LPDWORD pResumeHandle + ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ + +{ + NTSTATUS Status = STATUS_SUCCESS; + DWORD Level; + ULONG RecSize; + PVOID BufPtr = NULL; + ULONG BufSize = 0; + ULONG EntriesRead = 0; + ULONG TotalEntries = 0; + ULONG i = 0; + ULONG j = 0; + +#if DBG + if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC)) + dprintf(TEXT("LLS TRACE: LlsProductEnumW\n")); +#endif + + // + // Need to scan list so get read access. + // + RtlAcquireResourceShared(&MasterServiceListLock, TRUE); + + // + // Get size of each record based on info level. Only 0 and 1 supported. + // + Level = pProductInfo->Level; + if (Level == 0) + RecSize = sizeof(LLS_PRODUCT_INFO_0W); + else if (Level == 1) + RecSize = sizeof(LLS_PRODUCT_INFO_1W); + else { + Status = STATUS_INVALID_LEVEL; + goto LlsrProductEnumWExit; + } + + // + // Calculate how many records will fit into PrefMaxLen buffer. This is + // the record size * # records + space for the string data. If MAX_ULONG + // is passed in then we return all records. + // + i = *pResumeHandle; + while ((i < MasterServiceListSize) && (BufSize < pPrefMaxLen)) { + BufSize += RecSize; + EntriesRead++; + + i++; + } + + TotalEntries = EntriesRead; + + // + // If we overflowed the buffer then back up one record. + // + if (BufSize > pPrefMaxLen) { + BufSize -= RecSize; + EntriesRead--; + } + + if (i < MasterServiceListSize) + Status = STATUS_MORE_ENTRIES; + + // + // Now walk to the end of the list to see how many more records are still + // available. + // + TotalEntries += (MasterServiceListSize - i); + + // + // Reset Enum to correct place. + // + i = *pResumeHandle; + + // + // We now know how many records will fit into the buffer, so allocate space + // and fix up pointers so we can copy the information. + // + BufPtr = MIDL_user_allocate(BufSize); + if (BufPtr == NULL) { + Status = STATUS_NO_MEMORY; + goto LlsrProductEnumWExit; + } + + RtlZeroMemory((PVOID) BufPtr, BufSize); + + // + // Buffers are all setup, so loop through records and copy the data. + // + while ((j < EntriesRead) && (i < MasterServiceListSize)) { + if (Level == 0) + ((PLLS_PRODUCT_INFO_0) BufPtr)[j].Product = MasterServiceList[i]->Name; + else { + ((PLLS_PRODUCT_INFO_1) BufPtr)[j].Product = MasterServiceList[i]->Name; + ((PLLS_PRODUCT_INFO_1) BufPtr)[j].Purchased = MasterServiceList[i]->Licenses; + ((PLLS_PRODUCT_INFO_1) BufPtr)[j].InUse = MasterServiceList[i]->LicensesUsed; + ((PLLS_PRODUCT_INFO_1) BufPtr)[j].ConcurrentTotal = MasterServiceList[i]->MaxSessionCount; + ((PLLS_PRODUCT_INFO_1) BufPtr)[j].HighMark = MasterServiceList[i]->HighMark; + } + + i++; j++; + } + +LlsrProductEnumWExit: +#if DBG + if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC)) + dprintf(TEXT(" TotalEntries: %lu EntriesRead: %lu ResumeHandle: 0x%lX\n"), TotalEntries, EntriesRead, i); +#endif + *pTotalEntries = TotalEntries; + + *pResumeHandle = (ULONG) i; + if (Level == 0) { + pProductInfo->LlsProductInfo.Level0->EntriesRead = EntriesRead; + pProductInfo->LlsProductInfo.Level0->Buffer = (PLLS_PRODUCT_INFO_0W) BufPtr; + } else { + pProductInfo->LlsProductInfo.Level1->EntriesRead = EntriesRead; + pProductInfo->LlsProductInfo.Level1->Buffer = (PLLS_PRODUCT_INFO_1W) BufPtr; + } + + RtlReleaseResource(&MasterServiceListLock); + return Status; + +} // LlsrProductEnumW + + +///////////////////////////////////////////////////////////////////////// +NTSTATUS LlsrProductEnumA( + LLS_HANDLE Handle, + PLLS_PRODUCT_ENUM_STRUCTA ProductInfo, + DWORD PrefMaxLen, + LPDWORD TotalEntries, + LPDWORD ResumeHandle + ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ + +{ +#if DBG + if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC)) + dprintf(TEXT("LLS TRACE: LlsProductEnumA\n")); +#endif + + return STATUS_NOT_SUPPORTED; +} // LlsrProductEnumA + + +///////////////////////////////////////////////////////////////////////// +NTSTATUS LlsrProductAddW( + LLS_HANDLE Handle, + LPWSTR ProductFamily, + LPWSTR Product, + LPWSTR lpVersion + ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + PMASTER_SERVICE_RECORD Service; + DWORD Version; + +#if DBG + if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC)) + dprintf(TEXT("LLS TRACE: LlsProductAddW\n")); +#endif + + if ((ProductFamily == NULL) || (Product == NULL) || (lpVersion == NULL)) + return STATUS_INVALID_PARAMETER; + + Version = VersionToDWORD(lpVersion); + Service = MasterServiceListAdd(ProductFamily, Product, Version); + + if (Service == NULL) + return STATUS_NO_MEMORY; + + return STATUS_SUCCESS; +} // LlsrProductAddW + + +///////////////////////////////////////////////////////////////////////// +NTSTATUS LlsrProductAddA( + LLS_HANDLE Handle, + IN LPSTR ProductFamily, + IN LPSTR Product, + IN LPSTR Version + ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ +#if DBG + if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC)) + dprintf(TEXT("LLS TRACE: LlsProductAddA\n")); +#endif + + return STATUS_NOT_SUPPORTED; +} // LlsrProductAddA + + +///////////////////////////////////////////////////////////////////////// +NTSTATUS LlsrProductUserEnumW( + LLS_HANDLE Handle, + LPWSTR Product, + PLLS_PRODUCT_USER_ENUM_STRUCTW pProductUserInfo, + DWORD pPrefMaxLen, + LPDWORD pTotalEntries, + LPDWORD pResumeHandle + ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ + +{ + NTSTATUS Status = STATUS_SUCCESS; + DWORD Level; + ULONG RecSize; + PVOID BufPtr = NULL; + ULONG BufSize = 0; + ULONG EntriesRead = 0; + ULONG TotalEntries = 0; + ULONG i = 0; + PUSER_RECORD UserRec = NULL; + PVOID RestartKey = NULL; + PSVC_RECORD pService; + DWORD Flags; + ULONG j, AccessCount; + DWORD LastAccess; + +#if DBG + if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC)) + dprintf(TEXT("LLS TRACE: LlsProductUserEnumW\n")); +#endif + + if (Product == NULL) + return STATUS_INVALID_PARAMETER; + + // + // Need AddEnum lock, but just shared UserListLock (as we just read + // the data). + // + RtlAcquireResourceExclusive(&UserListAddEnumLock, TRUE); + RtlAcquireResourceShared(&UserListLock, TRUE); + + // + // Reset Enum to correct place. + // + RestartKey = (PVOID) *pResumeHandle; + UserRec = (PUSER_RECORD) RtlEnumerateGenericTableWithoutSplaying(&UserList, (VOID **) &RestartKey); + + // + // Get size of each record based on info level. Only 0 and 1 supported. + // + Level = pProductUserInfo->Level; + if (Level == 0) + RecSize = sizeof(LLS_PRODUCT_USER_INFO_0); + else if (Level == 1) + RecSize = sizeof(LLS_PRODUCT_USER_INFO_1); + else { + Status = STATUS_INVALID_LEVEL; + goto LlsrProductUserEnumWExit; + } + + // + // Calculate how many records will fit into PrefMaxLen buffer. This is + // the record size * # records + space for the string data. If MAX_ULONG + // is passed in then we return all records. + // + if (lstrcmpi(Product, BackOfficeStr)) + while ((UserRec != NULL) && (BufSize < pPrefMaxLen)) { + if ( !(UserRec->Flags & LLS_FLAG_DELETED) ) { + RtlEnterCriticalSection(&UserRec->ServiceTableLock); + pService = SvcListFind( Product, UserRec->Services, UserRec->ServiceTableSize ); + RtlLeaveCriticalSection(&UserRec->ServiceTableLock); + + if (pService != NULL) { + BufSize += RecSize; + EntriesRead++; + } + } + + // Get next record + UserRec = (PUSER_RECORD) RtlEnumerateGenericTableWithoutSplaying(&UserList, (VOID **) &RestartKey); + } + else + while ((UserRec != NULL) && (BufSize < pPrefMaxLen)) { + if (UserRec->Mapping != NULL) + Flags = UserRec->Mapping->Flags; + else + Flags = UserRec->Flags; + + if (!(UserRec->Flags & LLS_FLAG_DELETED)) + if (Flags & LLS_FLAG_SUITE_USE) { + BufSize += RecSize; + EntriesRead++; + } + + // Get next record + UserRec = (PUSER_RECORD) RtlEnumerateGenericTableWithoutSplaying(&UserList, (VOID **) &RestartKey); + } + + TotalEntries = EntriesRead; + + // + // If we overflowed the buffer then back up one record. + // + if (BufSize > pPrefMaxLen) { + BufSize -= RecSize; + EntriesRead--; + } + + if (UserRec != NULL) + Status = STATUS_MORE_ENTRIES; + + // + // Now walk to the end of the list to see how many more records are still + // available. + // + while (UserRec != NULL) { + TotalEntries++; + + UserRec = (PUSER_RECORD) RtlEnumerateGenericTableWithoutSplaying(&UserList, (VOID **) &RestartKey); + } + + // + // Reset Enum to correct place. + // + RestartKey = (PVOID) *pResumeHandle; + UserRec = (PUSER_RECORD) RtlEnumerateGenericTableWithoutSplaying(&UserList, (VOID **) &RestartKey); + + // + // We now know how many records will fit into the buffer, so allocate space + // and fix up pointers so we can copy the information. + // + BufPtr = MIDL_user_allocate(BufSize); + if (BufPtr == NULL) { + Status = STATUS_NO_MEMORY; + goto LlsrProductUserEnumWExit; + } + + RtlZeroMemory((PVOID) BufPtr, BufSize); + + // + // Buffers are all setup, so loop through records and copy the data. + // + if (lstrcmpi(Product, BackOfficeStr)) + while ((i < EntriesRead) && (UserRec != NULL)) { + if (!(UserRec->Flags & LLS_FLAG_DELETED)) { + RtlEnterCriticalSection(&UserRec->ServiceTableLock); + pService = SvcListFind( Product, UserRec->Services, UserRec->ServiceTableSize ); + if (pService != NULL) { + + if (Level == 0) + ((PLLS_PRODUCT_USER_INFO_0) BufPtr)[i].User = (LPTSTR) UserRec->UserID; + else { + ((PLLS_PRODUCT_USER_INFO_1) BufPtr)[i].User = (LPTSTR) UserRec->UserID; + ((PLLS_PRODUCT_USER_INFO_1) BufPtr)[i].Flags = pService->Flags; + ((PLLS_PRODUCT_USER_INFO_1) BufPtr)[i].LastUsed = pService->LastAccess; + ((PLLS_PRODUCT_USER_INFO_1) BufPtr)[i].UsageCount = pService->AccessCount; + } + + i++; + } + + RtlLeaveCriticalSection(&UserRec->ServiceTableLock); + } + + UserRec = (PUSER_RECORD) RtlEnumerateGenericTableWithoutSplaying(&UserList, (VOID **) &RestartKey); + } + else + while ((i < EntriesRead) && (UserRec != NULL)) { + if (!(UserRec->Flags & LLS_FLAG_DELETED)) { + if (UserRec->Mapping != NULL) + Flags = UserRec->Mapping->Flags; + else + Flags = UserRec->Flags; + + if (!(UserRec->Flags & LLS_FLAG_DELETED)) + if (Flags & LLS_FLAG_SUITE_USE) { + AccessCount = 0; + LastAccess = 0; + + RtlEnterCriticalSection(&UserRec->ServiceTableLock); + for (j = 0; j < UserRec->ServiceTableSize; j++) { + if (UserRec->Services[j].LastAccess > LastAccess) + LastAccess = UserRec->Services[j].LastAccess; + + if (UserRec->Services[j].AccessCount > AccessCount) + AccessCount = UserRec->Services[j].AccessCount; + } + + RtlLeaveCriticalSection(&UserRec->ServiceTableLock); + if (Level == 0) + ((PLLS_PRODUCT_USER_INFO_0) BufPtr)[i].User = (LPTSTR) UserRec->UserID; + else { + ((PLLS_PRODUCT_USER_INFO_1) BufPtr)[i].User = (LPTSTR) UserRec->UserID; + ((PLLS_PRODUCT_USER_INFO_1) BufPtr)[i].Flags = UserRec->Flags; + ((PLLS_PRODUCT_USER_INFO_1) BufPtr)[i].LastUsed = LastAccess; + ((PLLS_PRODUCT_USER_INFO_1) BufPtr)[i].UsageCount = AccessCount; + } + + i++; + } + + } + + UserRec = (PUSER_RECORD) RtlEnumerateGenericTableWithoutSplaying(&UserList, (VOID **) &RestartKey); + } + +LlsrProductUserEnumWExit: +#if DBG + if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC)) + dprintf(TEXT(" TotalEntries: %lu EntriesRead: %lu ResumeHandle: 0x%lX\n"), TotalEntries, EntriesRead, RestartKey); +#endif + *pTotalEntries = TotalEntries; + *pResumeHandle = (ULONG) RestartKey; + pProductUserInfo->LlsProductUserInfo.Level0->EntriesRead = EntriesRead; + pProductUserInfo->LlsProductUserInfo.Level0->Buffer = (PLLS_PRODUCT_USER_INFO_0W) BufPtr; + + RtlReleaseResource(&UserListAddEnumLock); + RtlReleaseResource(&UserListLock); + return Status; +} // LlsrProductUserEnumW + + +///////////////////////////////////////////////////////////////////////// +NTSTATUS LlsrProductUserEnumA( + LLS_HANDLE Handle, + LPSTR Product, + PLLS_PRODUCT_USER_ENUM_STRUCTA ProductUserInfo, + DWORD PrefMaxLen, + LPDWORD TotalEntries, + LPDWORD ResumeHandle + ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ + +{ +#if DBG + if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC)) + dprintf(TEXT("LLS TRACE: LlsProductUserEnumA\n")); +#endif + + return STATUS_NOT_SUPPORTED; +} // LlsrProductUserEnumA + + +///////////////////////////////////////////////////////////////////////// +NTSTATUS LlsrProductServerEnumW( + LLS_HANDLE Handle, + LPTSTR Product, + PLLS_SERVER_PRODUCT_ENUM_STRUCTW pProductServerInfo, + DWORD pPrefMaxLen, + LPDWORD pTotalEntries, + LPDWORD pResumeHandle + ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ + +{ + NTSTATUS Status = STATUS_SUCCESS; + DWORD Level; + ULONG RecSize; + PVOID BufPtr = NULL; + ULONG BufSize = 0; + ULONG EntriesRead = 0; + ULONG TotalEntries = 0; + ULONG i = 0; + ULONG j; + ULONG RestartKey = 0; + PSERVER_SERVICE_RECORD pSvc; + +#if DBG + if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC)) + dprintf(TEXT("LLS TRACE: LlsProductServerEnumW\n")); +#endif + + if (Product == NULL) + return STATUS_INVALID_PARAMETER; + + // + // Reset Enum to correct place. + // + RestartKey = (ULONG) *pResumeHandle; + + // + // Get size of each record based on info level. Only 0 and 1 supported. + // + Level = pProductServerInfo->Level; + RtlAcquireResourceShared(&ServerListLock, TRUE); + if (Level == 0) + RecSize = sizeof(LLS_SERVER_PRODUCT_INFO_0); + else if (Level == 1) + RecSize = sizeof(LLS_SERVER_PRODUCT_INFO_1); + else { + Status = STATUS_INVALID_LEVEL; + goto LlsrProductServerEnumWExit; + } + + + // + // Calculate how many records will fit into PrefMaxLen buffer. This is + // the record size * # records + space for the string data. If MAX_ULONG + // is passed in then we return all records. + // + for (i = RestartKey; i < ServerListSize; i++) { + pSvc = ServerServiceListFind( Product, ServerList[i]->ServiceTableSize, ServerList[i]->Services ); + + if (pSvc != NULL) { + BufSize += RecSize; + EntriesRead++; + } + + } + + TotalEntries = EntriesRead; + + // + // We now know how many records will fit into the buffer, so allocate space + // and fix up pointers so we can copy the information. + // + BufPtr = MIDL_user_allocate(BufSize); + if (BufPtr == NULL) { + Status = STATUS_NO_MEMORY; + goto LlsrProductServerEnumWExit; + } + + RtlZeroMemory((PVOID) BufPtr, BufSize); + + // + // Buffers are all setup, so loop through records and copy the data. + // + j = 0; + for (i = RestartKey; i < ServerListSize; i++) { + pSvc = ServerServiceListFind( Product, ServerList[i]->ServiceTableSize, ServerList[i]->Services ); + + if (pSvc != NULL) { + + if (Level == 0) + ((PLLS_SERVER_PRODUCT_INFO_0) BufPtr)[j].Name = ServerList[i]->Name; + else { + ((PLLS_SERVER_PRODUCT_INFO_1) BufPtr)[j].Name = ServerList[i]->Name; + ((PLLS_SERVER_PRODUCT_INFO_1) BufPtr)[j].Flags = pSvc->Flags; + ((PLLS_SERVER_PRODUCT_INFO_1) BufPtr)[j].MaxUses = pSvc->MaxSessionCount; + ((PLLS_SERVER_PRODUCT_INFO_1) BufPtr)[j].MaxSetUses = pSvc->MaxSetSessionCount; + ((PLLS_SERVER_PRODUCT_INFO_1) BufPtr)[j].HighMark = pSvc->HighMark; + } + + j++; + } + + } + +LlsrProductServerEnumWExit: +#if DBG + if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC)) + dprintf(TEXT(" TotalEntries: %lu EntriesRead: %lu ResumeHandle: 0x%lX\n"), TotalEntries, EntriesRead, RestartKey); +#endif + *pTotalEntries = TotalEntries; + *pResumeHandle = (ULONG) RestartKey; + pProductServerInfo->LlsServerProductInfo.Level0->EntriesRead = EntriesRead; + pProductServerInfo->LlsServerProductInfo.Level0->Buffer = (PLLS_SERVER_PRODUCT_INFO_0W) BufPtr; + + RtlReleaseResource(&ServerListLock); + return Status; +} // LlsrProductServerEnumW + + +///////////////////////////////////////////////////////////////////////// +NTSTATUS LlsrProductServerEnumA( + LLS_HANDLE Handle, + LPSTR Product, + PLLS_SERVER_PRODUCT_ENUM_STRUCTA ProductServerInfo, + DWORD PrefMaxLen, + LPDWORD TotalEntries, + LPDWORD ResumeHandle + ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ + +{ +#if DBG + if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC)) + dprintf(TEXT("LLS TRACE: LlsProductServerEnumA\n")); +#endif + + return STATUS_NOT_SUPPORTED; +} // LlsrProductServerEnumA + + +///////////////////////////////////////////////////////////////////////// +NTSTATUS LlsrProductLicenseEnumW( + LLS_HANDLE Handle, + LPWSTR Product, + PLLS_PRODUCT_LICENSE_ENUM_STRUCTW pProductLicenseInfo, + DWORD pPrefMaxLen, + LPDWORD pTotalEntries, + LPDWORD pResumeHandle + ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ + +{ + NTSTATUS Status = STATUS_SUCCESS; + DWORD Level; + PVOID BufPtr = NULL; + ULONG BufSize = 0; + ULONG EntriesRead = 0; + ULONG TotalEntries = 0; + ULONG i = 0; + ULONG j = 0; + DWORD RecordSize; + +#if DBG + if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC)) + dprintf(TEXT("LLS TRACE: LlsProductLicenseEnumW\n")); +#endif + + Level = pProductLicenseInfo->Level; + + if ( 0 == Level ) + { + RecordSize = sizeof( LLS_PRODUCT_LICENSE_INFO_0W ); + } + else if ( 1 == Level ) + { + RecordSize = sizeof( LLS_PRODUCT_LICENSE_INFO_1W ); + } + else + { + return STATUS_INVALID_LEVEL; + } + + // + // Need to scan list so get read access. + // + RtlAcquireResourceShared(&LicenseListLock, TRUE); + + // + // Calculate how many records will fit into PrefMaxLen buffer. + // + i = *pResumeHandle; + while ( ( i < PurchaseListSize ) && ( BufSize < pPrefMaxLen ) ) + { + // level 0 enums return only per seat licenses for backwards compatibility + if ( ( ( ( PurchaseList[i].AllowedModes & LLS_LICENSE_MODE_ALLOW_PER_SEAT ) + && !lstrcmpi( PurchaseList[i].Service->ServiceName, Product ) ) + || ( ( PurchaseList[i].AllowedModes & LLS_LICENSE_MODE_ALLOW_PER_SERVER ) + && !lstrcmpi( PurchaseList[i].PerServerService->ServiceName, Product ) ) ) + && ( ( Level > 0 ) + || ( PurchaseList[i].AllowedModes & LLS_LICENSE_MODE_ALLOW_PER_SEAT ) ) ) + { + BufSize += RecordSize; + EntriesRead++; + } + + i++; + } + + TotalEntries = EntriesRead; + + // + // If we overflowed the buffer then back up one record. + // + if (BufSize > pPrefMaxLen) + { + BufSize -= RecordSize; + EntriesRead--; + } + + // + // Now walk to the end of the list to see how many more records are still + // available. + // + while ( i < PurchaseListSize ) + { + if ( ( ( ( PurchaseList[i].AllowedModes & LLS_LICENSE_MODE_ALLOW_PER_SEAT ) + && !lstrcmpi( PurchaseList[i].Service->ServiceName, Product ) ) + || ( ( PurchaseList[i].AllowedModes & LLS_LICENSE_MODE_ALLOW_PER_SERVER ) + && !lstrcmpi( PurchaseList[i].PerServerService->ServiceName, Product ) ) ) + && ( ( Level > 0 ) + || ( PurchaseList[i].AllowedModes & LLS_LICENSE_MODE_ALLOW_PER_SEAT ) ) ) + { + TotalEntries++; + } + + i++; + } + + if (TotalEntries > EntriesRead) + Status = STATUS_MORE_ENTRIES; + + // + // Reset Enum to correct place. + // + i = *pResumeHandle; + + // + // We now know how many records will fit into the buffer, so allocate space + // and fix up pointers so we can copy the information. + // + BufPtr = MIDL_user_allocate(BufSize); + if (BufPtr == NULL) { + Status = STATUS_NO_MEMORY; + goto LlsrLicenseEnumWExit; + } + + RtlZeroMemory((PVOID) BufPtr, BufSize); + + // + // Buffers are all setup, so loop through records and copy the data. + // + while ((j < EntriesRead) && (i < PurchaseListSize)) + { + if ( ( ( ( PurchaseList[i].AllowedModes & LLS_LICENSE_MODE_ALLOW_PER_SEAT ) + && !lstrcmpi( PurchaseList[i].Service->ServiceName, Product ) ) + || ( ( PurchaseList[i].AllowedModes & LLS_LICENSE_MODE_ALLOW_PER_SERVER ) + && !lstrcmpi( PurchaseList[i].PerServerService->ServiceName, Product ) ) ) + && ( ( Level > 0 ) + || ( PurchaseList[i].AllowedModes & LLS_LICENSE_MODE_ALLOW_PER_SEAT ) ) ) + { + if ( 0 == Level ) + { + ((PLLS_PRODUCT_LICENSE_INFO_0W) BufPtr)[j].Quantity = PurchaseList[i].NumberLicenses; + ((PLLS_PRODUCT_LICENSE_INFO_0W) BufPtr)[j].Date = PurchaseList[i].Date; + ((PLLS_PRODUCT_LICENSE_INFO_0W) BufPtr)[j].Admin = PurchaseList[i].Admin; + ((PLLS_PRODUCT_LICENSE_INFO_0W) BufPtr)[j].Comment = PurchaseList[i].Comment; + } + else + { + ((PLLS_PRODUCT_LICENSE_INFO_1W) BufPtr)[j].Quantity = PurchaseList[i].NumberLicenses; + ((PLLS_PRODUCT_LICENSE_INFO_1W) BufPtr)[j].MaxQuantity = PurchaseList[i].MaxQuantity; + ((PLLS_PRODUCT_LICENSE_INFO_1W) BufPtr)[j].Date = PurchaseList[i].Date; + ((PLLS_PRODUCT_LICENSE_INFO_1W) BufPtr)[j].Admin = PurchaseList[i].Admin; + ((PLLS_PRODUCT_LICENSE_INFO_1W) BufPtr)[j].Comment = PurchaseList[i].Comment; + ((PLLS_PRODUCT_LICENSE_INFO_1W) BufPtr)[j].AllowedModes = PurchaseList[i].AllowedModes; + ((PLLS_PRODUCT_LICENSE_INFO_1W) BufPtr)[j].CertificateID = PurchaseList[i].CertificateID; + ((PLLS_PRODUCT_LICENSE_INFO_1W) BufPtr)[j].Source = PurchaseList[i].Source; + ((PLLS_PRODUCT_LICENSE_INFO_1W) BufPtr)[j].ExpirationDate = PurchaseList[i].ExpirationDate; + memcpy( ((PLLS_PRODUCT_LICENSE_INFO_1W) BufPtr)[j].Secrets, PurchaseList[i].Secrets, LLS_NUM_SECRETS * sizeof( *PurchaseList[i].Secrets ) ); + } + + j++; + } + + i++; + } + +LlsrLicenseEnumWExit: +#if DBG + if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC)) + dprintf(TEXT(" TotalEntries: %lu EntriesRead: %lu ResumeHandle: 0x%lX\n"), TotalEntries, EntriesRead, i); +#endif + *pTotalEntries = TotalEntries; + + *pResumeHandle = (ULONG) i; + + if ( 0 == Level ) + { + pProductLicenseInfo->LlsProductLicenseInfo.Level0->EntriesRead = EntriesRead; + pProductLicenseInfo->LlsProductLicenseInfo.Level0->Buffer = (PLLS_PRODUCT_LICENSE_INFO_0W) BufPtr; + } + else + { + pProductLicenseInfo->LlsProductLicenseInfo.Level1->EntriesRead = EntriesRead; + pProductLicenseInfo->LlsProductLicenseInfo.Level1->Buffer = (PLLS_PRODUCT_LICENSE_INFO_1W) BufPtr; + } + + RtlReleaseResource(&LicenseListLock); + return Status; + +} // LlsrProductLicenseEnumW + + +///////////////////////////////////////////////////////////////////////// +NTSTATUS LlsrProductLicenseEnumA( + LLS_HANDLE Handle, + LPSTR Product, + PLLS_PRODUCT_LICENSE_ENUM_STRUCTA ProductLicenseInfo, + DWORD PrefMaxLen, + LPDWORD TotalEntries, + LPDWORD ResumeHandle + ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ + +{ +#if DBG + if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC)) + dprintf(TEXT("LLS TRACE: LlsProductLicenseEnumA\n")); +#endif + + return STATUS_NOT_SUPPORTED; +} // LlsrProductLicenseEnumA + + +///////////////////////////////////////////////////////////////////////// +NTSTATUS LlsrUserEnumW( + LLS_HANDLE Handle, + PLLS_USER_ENUM_STRUCTW pUserInfo, + DWORD pPrefMaxLen, + LPDWORD pTotalEntries, + LPDWORD pResumeHandle + ) + +/*++ + +Routine Description: + + +Arguments: + + pPrefMaxLen - Supplies the number of bytes of information to return + in the buffer. If this value is MAXULONG, all available information + will be returned. + + pTotalEntries - Returns the total number of entries available. This value + is only valid if the return code is STATUS_SUCCESS or STATUS_MORE_ENTRIES. + + pResumeHandle - Supplies a handle to resume the enumeration from where it + left off the last time through. Returns the resume handle if return + code is STATUS_MORE_ENTRIES. + +Return Value: + + +--*/ + +{ + NTSTATUS Status = STATUS_SUCCESS; + DWORD Level; + ULONG RecSize; + PVOID BufPtr = NULL; + ULONG BufSize = 0; + ULONG EntriesRead = 0; + ULONG TotalEntries = 0; + ULONG i = 0; + ULONG j; + PUSER_RECORD UserRec = NULL; + PVOID RestartKey = NULL; + ULONG StrSize; + LPTSTR ProductString = NULL; + +#if DBG + if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC)) + dprintf(TEXT("LLS TRACE: LlsUserEnumW\n")); +#endif + + // + // Need AddEnum lock, but just shared UserListLock (as we just read + // the data). + // + RtlAcquireResourceExclusive(&UserListAddEnumLock, TRUE); + RtlAcquireResourceShared(&UserListLock, TRUE); + + // + // Reset Enum to correct place. + // + RestartKey = (PVOID) *pResumeHandle; + UserRec = (PUSER_RECORD) RtlEnumerateGenericTableWithoutSplaying(&UserList, (VOID **) &RestartKey); + + // + // Get size of each record based on info level. Only 0 and 1 supported. + // + Level = pUserInfo->Level; + if (Level == 0) + RecSize = sizeof(LLS_USER_INFO_0); + else if (Level == 1) + RecSize = sizeof(LLS_USER_INFO_1); + else if (Level == 2) + RecSize = sizeof(LLS_USER_INFO_2); + else { + Status = STATUS_INVALID_LEVEL; + goto LlsrUserEnumWExit; + } + + // + // Calculate how many records will fit into PrefMaxLen buffer. This is + // the record size * # records + space for the string data. If MAX_ULONG + // is passed in then we return all records. + // + while ((UserRec != NULL) && (BufSize < pPrefMaxLen)) { + if (!(UserRec->Flags & LLS_FLAG_DELETED)) { + BufSize += RecSize; + EntriesRead++; + } + + // Get next record + UserRec = (PUSER_RECORD) RtlEnumerateGenericTableWithoutSplaying(&UserList, (VOID **) &RestartKey); + } + + TotalEntries = EntriesRead; + + // + // If we overflowed the buffer then back up one record. + // + if (BufSize > pPrefMaxLen) { + BufSize -= RecSize; + EntriesRead--; + } + + if (UserRec != NULL) + Status = STATUS_MORE_ENTRIES; + + // + // Now walk to the end of the list to see how many more records are still + // available. + // + while (UserRec != NULL) { + TotalEntries++; + + UserRec = (PUSER_RECORD) RtlEnumerateGenericTableWithoutSplaying(&UserList, (VOID **) &RestartKey); + } + + // + // Reset Enum to correct place. + // + RestartKey = (PVOID) *pResumeHandle; + UserRec = (PUSER_RECORD) RtlEnumerateGenericTableWithoutSplaying(&UserList, (VOID **) &RestartKey); + + // + // We now know how many records will fit into the buffer, so allocate space + // and fix up pointers so we can copy the information. + // + BufPtr = MIDL_user_allocate(BufSize); + if (BufPtr == NULL) { + Status = STATUS_NO_MEMORY; + goto LlsrUserEnumWExit; + } + + RtlZeroMemory((PVOID) BufPtr, BufSize); + + // + // Buffers are all setup, so loop through records and copy the data. + // + while ((i < EntriesRead) && (UserRec != NULL)) { + if (!(UserRec->Flags & LLS_FLAG_DELETED)) { + + if (Level == 0) + ((PLLS_USER_INFO_0) BufPtr)[i].Name = (LPTSTR) UserRec->UserID; + else if (Level == 1) { + ((PLLS_USER_INFO_1) BufPtr)[i].Name = (LPTSTR) UserRec->UserID; + + if (UserRec->Mapping != NULL) + ((PLLS_USER_INFO_1) BufPtr)[i].Group = UserRec->Mapping->Name; + else + ((PLLS_USER_INFO_1) BufPtr)[i].Group = NULL; + + ((PLLS_USER_INFO_1) BufPtr)[i].Licensed = UserRec->LicensedProducts; + ((PLLS_USER_INFO_1) BufPtr)[i].UnLicensed = UserRec->ServiceTableSize - UserRec->LicensedProducts; + + ((PLLS_USER_INFO_1) BufPtr)[i].Flags = UserRec->Flags; + } else { + ((PLLS_USER_INFO_2) BufPtr)[i].Name = (LPTSTR) UserRec->UserID; + + if (UserRec->Mapping != NULL) + ((PLLS_USER_INFO_2) BufPtr)[i].Group = UserRec->Mapping->Name; + else + ((PLLS_USER_INFO_2) BufPtr)[i].Group = NULL; + + ((PLLS_USER_INFO_2) BufPtr)[i].Licensed = UserRec->LicensedProducts; + ((PLLS_USER_INFO_2) BufPtr)[i].UnLicensed = UserRec->ServiceTableSize - UserRec->LicensedProducts; + + ((PLLS_USER_INFO_2) BufPtr)[i].Flags = UserRec->Flags; + + // + // Walk product table and build up product string + // + RtlEnterCriticalSection(&UserRec->ServiceTableLock); + StrSize = 0; + + for (j = 0; j < UserRec->ServiceTableSize; j++) + StrSize += ((lstrlen(UserRec->Services[j].Service->Name) + 2) * sizeof(TCHAR)); + + if (StrSize != 0) { + ProductString = MIDL_user_allocate(StrSize); + if (ProductString != NULL) { + lstrcpy(ProductString, TEXT("")); + + for (j = 0; j < UserRec->ServiceTableSize; j++) { + if (j != 0) + lstrcat(ProductString, TEXT(", ")); + + lstrcat(ProductString, UserRec->Services[j].Service->Name); + } + + ((PLLS_USER_INFO_2) BufPtr)[i].Products = ProductString; + } + } + + if ((StrSize == 0) || (ProductString == NULL)) { + ProductString = MIDL_user_allocate(2 * sizeof(TCHAR)); + if (ProductString != NULL) { + lstrcpy(ProductString, TEXT("")); + ((PLLS_USER_INFO_2) BufPtr)[i].Products = ProductString; + } + } + + RtlLeaveCriticalSection(&UserRec->ServiceTableLock); + } + + i++; + } + + UserRec = (PUSER_RECORD) RtlEnumerateGenericTableWithoutSplaying(&UserList, (VOID **) &RestartKey); + } + +LlsrUserEnumWExit: +#if DBG + if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC)) + dprintf(TEXT(" TotalEntries: %lu EntriesRead: %lu ResumeHandle: 0x%lX\n"), TotalEntries, EntriesRead, RestartKey); +#endif + + *pTotalEntries = TotalEntries; + *pResumeHandle = (ULONG) RestartKey; + pUserInfo->LlsUserInfo.Level0->EntriesRead = EntriesRead; + pUserInfo->LlsUserInfo.Level0->Buffer = (PLLS_USER_INFO_0W) BufPtr; + + RtlReleaseResource(&UserListAddEnumLock); + RtlReleaseResource(&UserListLock); + return Status; +} // LlsrUserEnumW + + +///////////////////////////////////////////////////////////////////////// +NTSTATUS LlsrUserEnumA( + LLS_HANDLE Handle, + PLLS_USER_ENUM_STRUCTA UserInfo, + DWORD PrefMaxLen, + LPDWORD TotalEntries, + LPDWORD ResumeHandle + ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ + +{ +#if DBG + if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC)) + dprintf(TEXT("LLS TRACE: LlsUserEnumA\n")); +#endif + + return STATUS_NOT_SUPPORTED; +} // LlsrUserEnumA + + +///////////////////////////////////////////////////////////////////////// +NTSTATUS LlsrUserInfoGetW( + LLS_HANDLE Handle, + LPWSTR User, + DWORD Level, + PLLS_USER_INFOW *BufPtr + ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ + +{ + PUSER_RECORD UserRec = NULL; + PLLS_USER_INFOW pUser = NULL; + +#if DBG + if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC)) + dprintf(TEXT("LLS TRACE: LlsUserInfoGetW\n")); +#endif + + if (Level != 1) + return STATUS_INVALID_LEVEL; + + if ((User == NULL) || (BufPtr == NULL)) + return STATUS_INVALID_PARAMETER; + + RtlAcquireResourceShared(&UserListLock, TRUE); + + UserRec = UserListFind(User); + + if (UserRec != NULL) { + pUser = MIDL_user_allocate(sizeof(LLS_USER_INFOW)); + if (pUser != NULL) { + pUser->UserInfo1.Name = (LPTSTR) UserRec->UserID; + + if (UserRec->Mapping != NULL) { + pUser->UserInfo1.Mapping = UserRec->Mapping->Name; + pUser->UserInfo1.Licensed = UserRec->Mapping->Licenses; + pUser->UserInfo1.UnLicensed = 0; + } else { + pUser->UserInfo1.Mapping = NULL; + pUser->UserInfo1.Licensed = 1; + pUser->UserInfo1.UnLicensed = 0; + } + + pUser->UserInfo1.Flags = UserRec->Flags; + } + } + + RtlReleaseResource(&UserListLock); + + if (UserRec == NULL) + return STATUS_OBJECT_NAME_NOT_FOUND; + + if (pUser == NULL) + return STATUS_NO_MEMORY; + + *BufPtr = (PLLS_USER_INFOW) pUser; + return STATUS_SUCCESS; +} // LlsrUserInfoGetW + + +///////////////////////////////////////////////////////////////////////// +NTSTATUS LlsrUserInfoGetA( + LLS_HANDLE Handle, + LPSTR User, + DWORD Level, + PLLS_USER_INFOA *BufPtr + ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ + +{ +#if DBG + if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC)) + dprintf(TEXT("LLS TRACE: LlsUserInfoGetA\n")); +#endif + + return STATUS_NOT_SUPPORTED; +} // LlsrUserInfoGetA + + +///////////////////////////////////////////////////////////////////////// +NTSTATUS LlsrUserInfoSetW( + LLS_HANDLE Handle, + LPWSTR User, + DWORD Level, + PLLS_USER_INFOW BufPtr + ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ + +{ + NTSTATUS Status; + PUSER_RECORD UserRec = NULL; + PLLS_USER_INFO_1 pUser; + PMAPPING_RECORD pMap; + +#if DBG + if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC)) + dprintf(TEXT("LLS TRACE: LlsUserInfoSetW\n")); +#endif + + if (Level != 1) + return STATUS_INVALID_LEVEL; + + if ((User == NULL) || (BufPtr == NULL)) + return STATUS_INVALID_PARAMETER; + + RtlAcquireResourceShared(&UserListLock, TRUE); + + UserRec = UserListFind(User); + + if (UserRec != NULL) { + pUser = (PLLS_USER_INFO_1) BufPtr; + + // + // If in a mapping can't change SUITE_USE, since it is based on the + // License Group + // + if (UserRec->Mapping != NULL) { + RtlReleaseResource(&UserListLock); + return STATUS_MEMBER_IN_GROUP; + } + + RtlConvertSharedToExclusive(&UserListLock); + + // + // Reset SUITE_USE and turn off SUITE_AUTO + // + pUser->Flags &= LLS_FLAG_SUITE_USE; + UserRec->Flags &= ~LLS_FLAG_SUITE_USE; + UserRec->Flags |= pUser->Flags; + UserRec->Flags &= ~LLS_FLAG_SUITE_AUTO; + + // + // Run though and clean up all old licenses + // + UserLicenseListFree( UserRec ); + + // + // Now assign new ones + // + SvcListLicenseUpdate( UserRec ); + + } + + RtlReleaseResource(&UserListLock); + + if (UserRec == NULL) + Status = STATUS_OBJECT_NAME_NOT_FOUND; + else + Status = LLSDataSave(); + + return Status; +} // LlsrUserInfoSetW + + +///////////////////////////////////////////////////////////////////////// +NTSTATUS LlsrUserInfoSetA( + LLS_HANDLE Handle, + LPSTR User, + DWORD Level, + PLLS_USER_INFOA BufPtr + ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ + +{ +#if DBG + if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC)) + dprintf(TEXT("LLS TRACE: LlsUserInfoSetA\n")); +#endif + + return STATUS_NOT_SUPPORTED; +} // LlsrUserInfoSetA + + +///////////////////////////////////////////////////////////////////////// +NTSTATUS LlsrUserDeleteW( + LLS_HANDLE Handle, + LPTSTR User + ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ + +{ + NTSTATUS Status; + PUSER_RECORD UserRec = NULL; + PLLS_USER_INFO_1 pUser; + PMAPPING_RECORD pMap; + +#if DBG + if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC)) + if (User != NULL) + dprintf(TEXT("LLS TRACE: LlsUserDeleteW: %s\n"), User); + else + dprintf(TEXT("LLS TRACE: LlsUserDeleteW: <NULL>\n")); +#endif + + if (User == NULL) + return STATUS_INVALID_PARAMETER; + + RtlAcquireResourceShared(&UserListLock, TRUE); + + UserRec = UserListFind(User); + + if (UserRec != NULL) { + RtlConvertSharedToExclusive(&UserListLock); + + UserRec->Flags |= LLS_FLAG_DELETED; + UsersDeleted = TRUE; + SvcListLicenseFree(UserRec); + UserLicenseListFree(UserRec); + + if (UserRec->Services != NULL) + LocalFree(UserRec->Services); + + UserRec->Services = NULL; + UserRec->ServiceTableSize = 0; + } + + RtlReleaseResource(&UserListLock); + + if (UserRec == NULL) + Status = STATUS_OBJECT_NAME_NOT_FOUND; + else + Status = LLSDataSave(); + + return Status; + +} // LlsrUserDeleteW + + +///////////////////////////////////////////////////////////////////////// +NTSTATUS LlsrUserDeleteA( + LLS_HANDLE Handle, + LPSTR User + ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ + +{ +#if DBG + if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC)) + dprintf(TEXT("LLS TRACE: LlsUserDeleteA\n")); +#endif + + return STATUS_NOT_SUPPORTED; +} // LlsrUserDeleteA + + +///////////////////////////////////////////////////////////////////////// +NTSTATUS LlsrUserProductEnumW( + LLS_HANDLE Handle, + LPWSTR pUser, + PLLS_USER_PRODUCT_ENUM_STRUCTW pUserProductInfo, + DWORD pPrefMaxLen, + LPDWORD pTotalEntries, + LPDWORD pResumeHandle + ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ + +{ + NTSTATUS Status = STATUS_SUCCESS; + DWORD Level; + ULONG RecSize; + PVOID BufPtr = NULL; + ULONG BufSize = 0; + ULONG EntriesRead = 0; + ULONG TotalEntries = 0; + ULONG i = 0; + ULONG j = 0; + PUSER_RECORD UserRec = NULL; + +#if DBG + if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC)) + dprintf(TEXT("LLS TRACE: LlsUserProductEnumW\n")); +#endif + + if ( pUser == NULL) + return STATUS_INVALID_PARAMETER; + + // + // Get size of each record based on info level. Only 0 and 1 supported. + // + Level = pUserProductInfo->Level; + if (Level == 0) + RecSize = sizeof(LLS_USER_PRODUCT_INFO_0); + else if (Level == 1) + RecSize = sizeof(LLS_USER_PRODUCT_INFO_1); + else { + return STATUS_INVALID_LEVEL; + } + + // + // Need to find the user-rec + // + RtlAcquireResourceShared(&UserListLock, TRUE); + + // + // Reset Enum to correct place. + // + UserRec = UserListFind(pUser); + if (UserRec == NULL) { + Status = STATUS_OBJECT_NAME_NOT_FOUND; + goto LlsrUserProductEnumWExit; + } + + i = (ULONG) *pResumeHandle; + RtlEnterCriticalSection(&UserRec->ServiceTableLock); + + // + // Calculate how many records will fit into PrefMaxLen buffer. This is + // the record size * # records + space for the string data. If MAX_ULONG + // is passed in then we return all records. + // + while ((i < UserRec->ServiceTableSize) && (BufSize < pPrefMaxLen)) { + BufSize += RecSize; + EntriesRead++; + i++; + } + + TotalEntries = EntriesRead; + + // + // If we overflowed the buffer then back up one record. + // + if (BufSize > pPrefMaxLen) { + BufSize -= RecSize; + EntriesRead--; + } + + if (i < UserRec->ServiceTableSize) + Status = STATUS_MORE_ENTRIES; + + // + // Now walk to the end of the list to see how many more records are still + // available. + // + TotalEntries += (UserRec->ServiceTableSize - i); + + // + // We now know how many records will fit into the buffer, so allocate space + // and fix up pointers so we can copy the information. + // + BufPtr = MIDL_user_allocate(BufSize); + if (BufPtr == NULL) { + Status = STATUS_NO_MEMORY; + RtlLeaveCriticalSection(&UserRec->ServiceTableLock); + goto LlsrUserProductEnumWExit; + } + + RtlZeroMemory((PVOID) BufPtr, BufSize); + + // + // Buffers are all setup, so loop through records and copy the data. + // + j = 0; + i = (ULONG) *pResumeHandle; + while ((j < EntriesRead) && (i < UserRec->ServiceTableSize)) { + + if (Level == 0) + ((PLLS_USER_PRODUCT_INFO_0) BufPtr)[j].Product = UserRec->Services[i].Service->Name; + else { + ((PLLS_USER_PRODUCT_INFO_1) BufPtr)[j].Product = UserRec->Services[i].Service->Name; + ((PLLS_USER_PRODUCT_INFO_1) BufPtr)[j].Flags = UserRec->Services[i].Flags; + ((PLLS_USER_PRODUCT_INFO_1) BufPtr)[j].LastUsed = UserRec->Services[i].LastAccess; + ((PLLS_USER_PRODUCT_INFO_1) BufPtr)[j].UsageCount = UserRec->Services[i].AccessCount; + } + + i++; j++; + } + + RtlLeaveCriticalSection(&UserRec->ServiceTableLock); + +LlsrUserProductEnumWExit: +#if DBG + if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC)) + dprintf(TEXT(" TotalEntries: %lu EntriesRead: %lu ResumeHandle: 0x%lX\n"), TotalEntries, EntriesRead, i); +#endif + *pTotalEntries = TotalEntries; + *pResumeHandle = (ULONG) i; + pUserProductInfo->LlsUserProductInfo.Level0->EntriesRead = EntriesRead; + pUserProductInfo->LlsUserProductInfo.Level0->Buffer = (PLLS_USER_PRODUCT_INFO_0W) BufPtr; + + RtlReleaseResource(&UserListLock); + return Status; +} // LlsrUserProductEnumW + + +///////////////////////////////////////////////////////////////////////// +NTSTATUS LlsrUserProductEnumA( + LLS_HANDLE Handle, + LPSTR User, + PLLS_USER_PRODUCT_ENUM_STRUCTA UserProductInfo, + DWORD PrefMaxLen, + LPDWORD TotalEntries, + LPDWORD ResumeHandle + ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ + +{ +#if DBG + if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC)) + dprintf(TEXT("LLS TRACE: LlsUserProductEnumA\n")); +#endif + + return STATUS_NOT_SUPPORTED; +} // LlsrUserProductEnumA + + +///////////////////////////////////////////////////////////////////////// +NTSTATUS LlsrUserProductDeleteW( + LLS_HANDLE Handle, + LPWSTR User, + LPWSTR Product + ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ + +{ + NTSTATUS Status; +#if DBG + if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC)) + dprintf(TEXT("LLS TRACE: LlsUserProductDeleteW\n")); +#endif + + if ((User == NULL) || (Product == NULL)) + return STATUS_INVALID_PARAMETER; + + RtlAcquireResourceShared(&UserListLock, TRUE); + Status = SvcListDelete(User, Product); + RtlReleaseResource(&UserListLock); + + if ( STATUS_SUCCESS == Status ) + { + // save modified data + Status = LLSDataSave(); + } + + return Status; +} // LlsrUserProductDeleteW + + +///////////////////////////////////////////////////////////////////////// +NTSTATUS LlsrUserProductDeleteA( + LLS_HANDLE Handle, + LPSTR User, + LPSTR Product + ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ + +{ +#if DBG + if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC)) + dprintf(TEXT("LLS TRACE: LlsUserProductDeleteA\n")); +#endif + + return STATUS_NOT_SUPPORTED; +} // LlsrUserProductDeleteA + + +///////////////////////////////////////////////////////////////////////// +NTSTATUS LlsrMappingEnumW( + LLS_HANDLE Handle, + PLLS_MAPPING_ENUM_STRUCTW pMappingInfo, + DWORD pPrefMaxLen, + LPDWORD pTotalEntries, + LPDWORD pResumeHandle + ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ + +{ + NTSTATUS Status = STATUS_SUCCESS; + DWORD Level; + ULONG RecSize; + PVOID BufPtr = NULL; + ULONG BufSize = 0; + ULONG EntriesRead = 0; + ULONG TotalEntries = 0; + ULONG i = 0; + ULONG j = 0; + +#if DBG + if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC)) + dprintf(TEXT("LLS TRACE: LlsMappingEnumW\n")); +#endif + + // + // Need to scan list so get read access. + // + RtlAcquireResourceShared(&MappingListLock, TRUE); + + // + // Get size of each record based on info level. Only 0 and 1 supported. + // + Level = pMappingInfo->Level; + if (Level == 0) + RecSize = sizeof(LLS_MAPPING_INFO_0W); + else if (Level == 1) + RecSize = sizeof(LLS_MAPPING_INFO_1W); + else { + Status = STATUS_INVALID_LEVEL; + goto LlsrMappingEnumWExit; + } + + // + // Calculate how many records will fit into PrefMaxLen buffer. This is + // the record size * # records + space for the string data. If MAX_ULONG + // is passed in then we return all records. + // + i = *pResumeHandle; + while ((i < MappingListSize) && (BufSize < pPrefMaxLen)) { + BufSize += RecSize; + EntriesRead++; + + i++; + } + + TotalEntries = EntriesRead; + + // + // If we overflowed the buffer then back up one record. + // + if (BufSize > pPrefMaxLen) { + BufSize -= RecSize; + EntriesRead--; + } + + if (i < MappingListSize) + Status = STATUS_MORE_ENTRIES; + + // + // Now walk to the end of the list to see how many more records are still + // available. + // + TotalEntries += (MappingListSize - i); + + // + // Reset Enum to correct place. + // + i = *pResumeHandle; + + // + // We now know how many records will fit into the buffer, so allocate space + // and fix up pointers so we can copy the information. + // + BufPtr = MIDL_user_allocate(BufSize); + if (BufPtr == NULL) { + Status = STATUS_NO_MEMORY; + goto LlsrMappingEnumWExit; + } + + RtlZeroMemory((PVOID) BufPtr, BufSize); + + // + // Buffers are all setup, so loop through records and copy the data. + // + j = 0; + while ((j < EntriesRead) && (i < MappingListSize)) { + if (Level == 0) + ((PLLS_GROUP_INFO_0) BufPtr)[j].Name = MappingList[i]->Name; + else { + ((PLLS_GROUP_INFO_1) BufPtr)[j].Name = MappingList[i]->Name; + ((PLLS_GROUP_INFO_1) BufPtr)[j].Comment = MappingList[i]->Comment; + ((PLLS_GROUP_INFO_1) BufPtr)[j].Licenses = MappingList[i]->Licenses; + } + + i++; j++; + } + +LlsrMappingEnumWExit: +#if DBG + if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC)) + dprintf(TEXT(" TotalEntries: %lu EntriesRead: %lu ResumeHandle: 0x%lX\n"), TotalEntries, EntriesRead, i); +#endif + *pTotalEntries = TotalEntries; + + *pResumeHandle = (ULONG) i; + if (Level == 0) { + pMappingInfo->LlsMappingInfo.Level0->EntriesRead = EntriesRead; + pMappingInfo->LlsMappingInfo.Level0->Buffer = (PLLS_MAPPING_INFO_0W) BufPtr; + } else { + pMappingInfo->LlsMappingInfo.Level1->EntriesRead = EntriesRead; + pMappingInfo->LlsMappingInfo.Level1->Buffer = (PLLS_MAPPING_INFO_1W) BufPtr; + } + + RtlReleaseResource(&MappingListLock); + return Status; + +} // LlsrMappingEnumW + + +///////////////////////////////////////////////////////////////////////// +NTSTATUS LlsrMappingEnumA( + LLS_HANDLE Handle, + PLLS_MAPPING_ENUM_STRUCTA MappingInfo, + DWORD PrefMaxLen, + LPDWORD TotalEntries, + LPDWORD ResumeHandle + ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ + +{ +#if DBG + if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC)) + dprintf(TEXT("LLS TRACE: LlsMappingEnumA\n")); +#endif + + return STATUS_NOT_SUPPORTED; +} // LlsrMappingEnumA + + +///////////////////////////////////////////////////////////////////////// +NTSTATUS LlsrMappingInfoGetW( + LLS_HANDLE Handle, + LPWSTR Mapping, + DWORD Level, + PLLS_MAPPING_INFOW *BufPtr + ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ + +{ + PMAPPING_RECORD pMapping = NULL; + PLLS_GROUP_INFO_1 pMap = NULL; + +#if DBG + if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC)) + dprintf(TEXT("LLS TRACE: LlsMappingInfoGetW\n")); +#endif + + if (Level != 1) + return STATUS_INVALID_LEVEL; + + if ((Mapping == NULL) || (BufPtr == NULL)) + return STATUS_INVALID_PARAMETER; + + RtlAcquireResourceShared(&MappingListLock, TRUE); + + pMapping = MappingListFind(Mapping); + + if (pMapping != NULL) { + pMap = MIDL_user_allocate(sizeof(LLS_GROUP_INFO_1)); + if (pMap != NULL) { + pMap->Name = pMapping->Name; + pMap->Comment = pMapping->Comment; + pMap->Licenses = pMapping->Licenses; + } + } + + RtlReleaseResource(&MappingListLock); + + if (pMapping == NULL) + return STATUS_OBJECT_NAME_NOT_FOUND; + + if (pMap == NULL) + return STATUS_NO_MEMORY; + + *BufPtr = (PLLS_MAPPING_INFOW) pMap; + return STATUS_SUCCESS; + +} // LlsrMappingInfoGetW + + +///////////////////////////////////////////////////////////////////////// +NTSTATUS LlsrMappingInfoGetA( + LLS_HANDLE Handle, + LPSTR Mapping, + DWORD Level, + PLLS_MAPPING_INFOA *BufPtr + ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ + +{ +#if DBG + if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC)) + dprintf(TEXT("LLS TRACE: LlsMappingInfoGetA\n")); +#endif + + return STATUS_NOT_SUPPORTED; +} // LlsrMappingInfoGetA + + +///////////////////////////////////////////////////////////////////////// +NTSTATUS LlsrMappingInfoSetW( + LLS_HANDLE Handle, + LPWSTR Mapping, + DWORD Level, + PLLS_MAPPING_INFOW BufPtr + ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ + +{ + NTSTATUS Status; + PMAPPING_RECORD pMapping = NULL; + PLLS_GROUP_INFO_1 pMap; + LPTSTR NewComment; + +#if DBG + if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC)) + dprintf(TEXT("LLS TRACE: LlsMappingInfoSetW\n")); +#endif + + if (Level != 1) + return STATUS_INVALID_LEVEL; + + if ((Mapping == NULL) || (BufPtr == NULL)) + return STATUS_INVALID_PARAMETER; + + RtlAcquireResourceExclusive(&MappingListLock, TRUE); + + pMapping = MappingListFind(Mapping); + + if (pMapping != NULL) { + pMap = (PLLS_GROUP_INFO_1) BufPtr; + + // + // Check if comment has changed + // + if (pMap->Comment != NULL) + if (lstrcmp(pMap->Comment, pMapping->Comment)) { + NewComment = (LPTSTR) LocalAlloc(LPTR, (lstrlen(pMap->Comment) + 1) * sizeof(TCHAR)); + if (NewComment != NULL) { + LocalFree(pMapping->Comment); + pMapping->Comment = NewComment; + lstrcpy(pMapping->Comment, pMap->Comment); + } + } + + if ( pMapping->Licenses != pMap->Licenses ) + { + MappingLicenseListFree( pMapping ); + pMapping->Licenses = pMap->Licenses; + MappingLicenseUpdate( pMapping, TRUE ); + } + } + + RtlReleaseResource(&MappingListLock); + + if (pMapping == NULL) + Status = STATUS_OBJECT_NAME_NOT_FOUND; + else + Status = MappingListSave(); + + return Status; + +} // LlsrMappingInfoSetW + + +///////////////////////////////////////////////////////////////////////// +NTSTATUS LlsrMappingInfoSetA( + LLS_HANDLE Handle, + LPSTR Mapping, + DWORD Level, + PLLS_MAPPING_INFOA BufPtr + ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ + +{ +#if DBG + if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC)) + dprintf(TEXT("LLS TRACE: LlsMappingInfoSetA\n")); +#endif + + return STATUS_NOT_SUPPORTED; +} // LlsrMappingInfoSetA + + +///////////////////////////////////////////////////////////////////////// +NTSTATUS LlsrMappingUserEnumW( + LLS_HANDLE Handle, + LPWSTR Mapping, + PLLS_USER_ENUM_STRUCTW pMappingUserInfo, + DWORD pPrefMaxLen, + LPDWORD pTotalEntries, + LPDWORD pResumeHandle + ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ + +{ + NTSTATUS Status = STATUS_SUCCESS; + PMAPPING_RECORD pMapping; + DWORD Level; + PVOID BufPtr = NULL; + ULONG BufSize = 0; + ULONG EntriesRead = 0; + ULONG TotalEntries = 0; + ULONG i = 0; + ULONG j = 0; + +#if DBG + if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC)) + dprintf(TEXT("LLS TRACE: LlsMappingUserEnumW\n")); +#endif + + Level = pMappingUserInfo->Level; + if (Level != 0) + return STATUS_INVALID_LEVEL; + + // + // Need to scan list so get read access. + // + RtlAcquireResourceShared(&MappingListLock, TRUE); + + pMapping = MappingListFind(Mapping); + if (pMapping == NULL) { + Status = STATUS_OBJECT_NAME_NOT_FOUND; + goto LlsrMappingUserEnumWExit; + } + + // + // Calculate how many records will fit into PrefMaxLen buffer. This is + // the record size * # records + space for the string data. If MAX_ULONG + // is passed in then we return all records. + // + i = *pResumeHandle; + while ((i < pMapping->NumMembers) && (BufSize < pPrefMaxLen)) { + BufSize += sizeof(LLS_USER_INFO_0); + EntriesRead++; + + i++; + } + + TotalEntries = EntriesRead; + + // + // If we overflowed the buffer then back up one record. + // + if (BufSize > pPrefMaxLen) { + BufSize -= sizeof(LLS_USER_INFO_0); + EntriesRead--; + } + + if (i < pMapping->NumMembers) + Status = STATUS_MORE_ENTRIES; + + // + // Now walk to the end of the list to see how many more records are still + // available. + // + TotalEntries += (pMapping->NumMembers - i); + + // + // Reset Enum to correct place. + // + i = *pResumeHandle; + + // + // We now know how many records will fit into the buffer, so allocate space + // and fix up pointers so we can copy the information. + // + BufPtr = MIDL_user_allocate(BufSize); + if (BufPtr == NULL) { + Status = STATUS_NO_MEMORY; + goto LlsrMappingUserEnumWExit; + } + + RtlZeroMemory((PVOID) BufPtr, BufSize); + + // + // Buffers are all setup, so loop through records and copy the data. + // + while ((j < EntriesRead) && (i < pMapping->NumMembers)) { + ((PLLS_USER_INFO_0) BufPtr)[j].Name = pMapping->Members[i]; + i++; j++; + } + +LlsrMappingUserEnumWExit: +#if DBG + if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC)) + dprintf(TEXT(" TotalEntries: %lu EntriesRead: %lu ResumeHandle: 0x%lX\n"), TotalEntries, EntriesRead, i); +#endif + *pTotalEntries = TotalEntries; + + *pResumeHandle = (ULONG) i; + pMappingUserInfo->LlsUserInfo.Level0->EntriesRead = EntriesRead; + pMappingUserInfo->LlsUserInfo.Level0->Buffer = (PLLS_USER_INFO_0W) BufPtr; + + RtlReleaseResource(&MappingListLock); + return Status; + +} // LlsrMappingUserEnumW + + +///////////////////////////////////////////////////////////////////////// +NTSTATUS LlsrMappingUserEnumA( + LLS_HANDLE Handle, + LPSTR Mapping, + PLLS_USER_ENUM_STRUCTA MappingUserInfo, + DWORD PrefMaxLen, + LPDWORD TotalEntries, + LPDWORD ResumeHandle + ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ + +{ +#if DBG + if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC)) + dprintf(TEXT("LLS TRACE: LlsMappingUserEnumA\n")); +#endif + + return STATUS_NOT_SUPPORTED; +} // LlsrMappingUserEnumA + + +///////////////////////////////////////////////////////////////////////// +NTSTATUS LlsrMappingUserAddW( + LLS_HANDLE Handle, + LPWSTR Mapping, + LPWSTR User + ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ + +{ + NTSTATUS Status = STATUS_SUCCESS; + PUSER_RECORD pUserRec; + PMAPPING_RECORD pMap; + +#if DBG + if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC)) + dprintf(TEXT("LLS TRACE: LlsMappingUserAddW\n")); +#endif + + if ((Mapping == NULL) || (User == NULL)) + return STATUS_INVALID_PARAMETER; + + RtlAcquireResourceExclusive(&MappingListLock, TRUE); + pMap = MappingUserListAdd( Mapping, User ); + + if (pMap == NULL) + Status = STATUS_OBJECT_NAME_NOT_FOUND; + else { + RtlAcquireResourceShared(&UserListLock, TRUE); + pUserRec = UserListFind(User); + + if (pUserRec != NULL) + UserMappingAdd(pMap, pUserRec); + + RtlReleaseResource(&UserListLock); + } + + RtlReleaseResource(&MappingListLock); + + if (Status == STATUS_SUCCESS) + { + Status = MappingListSave(); + + if (Status == STATUS_SUCCESS) + Status = LLSDataSave(); + } + + return Status; + +} // LlsrMappingUserAddW + + +///////////////////////////////////////////////////////////////////////// +NTSTATUS LlsrMappingUserAddA( + LLS_HANDLE Handle, + LPSTR Mapping, + LPSTR User + ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ + +{ +#if DBG + if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC)) + dprintf(TEXT("LLS TRACE: LlsMappingUserAddA\n")); +#endif + + return STATUS_NOT_SUPPORTED; +} // LlsrMappingUserAddA + + +///////////////////////////////////////////////////////////////////////// +NTSTATUS LlsrMappingUserDeleteW( + LLS_HANDLE Handle, + LPWSTR Mapping, + LPWSTR User + ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ + +{ + NTSTATUS Status; + PUSER_RECORD pUser; + +#if DBG + if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC)) + dprintf(TEXT("LLS TRACE: LlsMappingUserDeleteW\n")); +#endif + + if ((Mapping == NULL) || (User == NULL)) + return STATUS_INVALID_PARAMETER; + + RtlAcquireResourceExclusive(&MappingListLock, TRUE); + Status = MappingUserListDelete(Mapping, User); + RtlReleaseResource(&MappingListLock); + + pUser = UserListFind( User ); + + if (pUser != NULL) { + // + // if auto switch to BackOffice then turn BackOffice off + // + if (pUser->Flags & LLS_FLAG_SUITE_AUTO) + pUser->Flags &= ~ LLS_FLAG_SUITE_USE; + + // + // Free up any licenses used by the user + // + SvcListLicenseFree( pUser ); + pUser->Mapping = NULL; + + // + // And claim any needed new-ones + // + SvcListLicenseUpdate( pUser ); + } + + if (Status == STATUS_SUCCESS) + { + Status = MappingListSave(); + + if (Status == STATUS_SUCCESS) + Status = LLSDataSave(); + } + + return Status; +} // LlsrMappingUserDeleteW + + +///////////////////////////////////////////////////////////////////////// +NTSTATUS LlsrMappingUserDeleteA( + LLS_HANDLE Handle, + LPSTR Mapping, + LPSTR User + ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ + +{ +#if DBG + if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC)) + dprintf(TEXT("LLS TRACE: LlsMappingUserDeleteA\n")); +#endif + + return STATUS_NOT_SUPPORTED; +} // LlsrMappingUserDeleteA + + +///////////////////////////////////////////////////////////////////////// +NTSTATUS LlsrMappingAddW( + LLS_HANDLE Handle, + DWORD Level, + PLLS_MAPPING_INFOW BufPtr + ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ + +{ + NTSTATUS Status = STATUS_SUCCESS; + PMAPPING_RECORD pMap; + +#if DBG + if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC)) + dprintf(TEXT("LLS TRACE: LlsMappingAddW\n")); +#endif + + if (Level != 1) + return STATUS_INVALID_LEVEL; + + if ((BufPtr == NULL) || + (BufPtr->MappingInfo1.Name == NULL) || + (BufPtr->MappingInfo1.Comment == NULL)) + return STATUS_INVALID_PARAMETER; + + RtlAcquireResourceExclusive(&MappingListLock, TRUE); + + pMap = MappingListAdd(BufPtr->MappingInfo1.Name, + BufPtr->MappingInfo1.Comment, + BufPtr->MappingInfo1.Licenses); + + RtlReleaseResource(&MappingListLock); + if (pMap == NULL) + Status = STATUS_NO_MEMORY; + + if (Status == STATUS_SUCCESS) + Status = MappingListSave(); + + return Status; +} // LlsrMappingAddW + + +///////////////////////////////////////////////////////////////////////// +NTSTATUS LlsrMappingAddA( + LLS_HANDLE Handle, + DWORD Level, + PLLS_MAPPING_INFOA BufPtr + ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ + +{ +#if DBG + if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC)) + dprintf(TEXT("LLS TRACE: LlsMappingAddA\n")); +#endif + + return STATUS_NOT_SUPPORTED; +} // LlsrMappingAddA + + +///////////////////////////////////////////////////////////////////////// +NTSTATUS LlsrMappingDeleteW( + LLS_HANDLE Handle, + LPWSTR Mapping + ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ + +{ + NTSTATUS Status; + +#if DBG + if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC)) + dprintf(TEXT("LLS TRACE: LlsMappingDeleteW\n")); +#endif + + if (Mapping == NULL) + return STATUS_INVALID_PARAMETER; + + RtlAcquireResourceExclusive(&MappingListLock, TRUE); + Status = MappingListDelete(Mapping); + RtlReleaseResource(&MappingListLock); + + if (Status == STATUS_SUCCESS) + Status = MappingListSave(); + + return Status; +} // LlsrMappingDeleteW + + +///////////////////////////////////////////////////////////////////////// +NTSTATUS LlsrMappingDeleteA( + LLS_HANDLE Handle, + LPSTR Mapping + ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ + +{ +#if DBG + if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC)) + dprintf(TEXT("LLS TRACE: LlsMappingDeleteA\n")); +#endif + + return STATUS_NOT_SUPPORTED; +} // LlsrMappingDeleteA + + +///////////////////////////////////////////////////////////////////////// +NTSTATUS LlsrServerEnumW( + LLS_HANDLE Handle, + LPWSTR Server, + PLLS_SERVER_ENUM_STRUCTW pServerProductInfo, + DWORD pPrefMaxLen, + LPDWORD pTotalEntries, + LPDWORD pResumeHandle + ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ + +{ +#if DBG + if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC)) + dprintf(TEXT("LLS TRACE: LlsServerEnumW\n")); +#endif + + return STATUS_NOT_SUPPORTED; +} // LlsrServerEnumW + + +///////////////////////////////////////////////////////////////////////// +NTSTATUS LlsrServerEnumA( + LLS_HANDLE Handle, + LPSTR Server, + PLLS_SERVER_ENUM_STRUCTA pServerProductInfo, + DWORD PrefMaxLen, + LPDWORD TotalEntries, + LPDWORD ResumeHandle + ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ + +{ +#if DBG + if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC)) + dprintf(TEXT("LLS TRACE: LlsServerEnumA\n")); +#endif + + return STATUS_NOT_SUPPORTED; +} // LlsrServerEnumA + + +///////////////////////////////////////////////////////////////////////// +NTSTATUS LlsrServerProductEnumW( + LLS_HANDLE Handle, + LPWSTR Server, + PLLS_SERVER_PRODUCT_ENUM_STRUCTW pServerProductInfo, + DWORD pPrefMaxLen, + LPDWORD pTotalEntries, + LPDWORD pResumeHandle + ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ + +{ +#if DBG + if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC)) + dprintf(TEXT("LLS TRACE: LlsServerProductEnumW\n")); +#endif + + return STATUS_NOT_SUPPORTED; +} // LlsrServerProductEnumW + + +///////////////////////////////////////////////////////////////////////// +NTSTATUS LlsrServerProductEnumA( + LLS_HANDLE Handle, + LPSTR Server, + PLLS_SERVER_PRODUCT_ENUM_STRUCTA pServerProductInfo, + DWORD PrefMaxLen, + LPDWORD TotalEntries, + LPDWORD ResumeHandle + ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ + +{ +#if DBG + if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC)) + dprintf(TEXT("LLS TRACE: LlsServerProductEnumA\n")); +#endif + + return STATUS_NOT_SUPPORTED; +} // LlsrServerProductEnumA + + +///////////////////////////////////////////////////////////////////////// +NTSTATUS LlsrLocalProductEnumW( + LLS_HANDLE Handle, + PLLS_SERVER_PRODUCT_ENUM_STRUCTW pServerProductInfo, + DWORD pPrefMaxLen, + LPDWORD pTotalEntries, + LPDWORD pResumeHandle + ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ + +{ +#if DBG + if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC)) + dprintf(TEXT("LLS TRACE: LlsLocalProductEnumW\n")); +#endif + + return STATUS_NOT_SUPPORTED; +} // LlsrLocalProductEnumW + + +///////////////////////////////////////////////////////////////////////// +NTSTATUS LlsrLocalProductEnumA( + LLS_HANDLE Handle, + PLLS_SERVER_PRODUCT_ENUM_STRUCTA pServerProductInfo, + DWORD PrefMaxLen, + LPDWORD TotalEntries, + LPDWORD ResumeHandle + ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ + +{ +#if DBG + if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC)) + dprintf(TEXT("LLS TRACE: LlsLocalProductEnumA\n")); +#endif + + return STATUS_NOT_SUPPORTED; +} // LlsrLocalProductEnumA + + +///////////////////////////////////////////////////////////////////////// +NTSTATUS LlsrLocalProductInfoGetW( + LLS_HANDLE Handle, + LPWSTR Product, + DWORD Level, + PLLS_SERVER_PRODUCT_INFOW *BufPtr + ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ + +{ +#if DBG + if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC)) + dprintf(TEXT("LLS TRACE: LlsLocalProductInfoGetW\n")); +#endif + + return STATUS_NOT_SUPPORTED; +} // LlsrLocalProductInfoGetW + + +///////////////////////////////////////////////////////////////////////// +NTSTATUS LlsrLocalProductInfoGetA( + LLS_HANDLE Handle, + LPSTR Product, + DWORD Level, + PLLS_SERVER_PRODUCT_INFOA *BufPtr + ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ + +{ +#if DBG + if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC)) + dprintf(TEXT("LLS TRACE: LlsLocalProductInfoGetA\n")); +#endif + + return STATUS_NOT_SUPPORTED; +} // LlsrLocalProductInfoGetA + + +///////////////////////////////////////////////////////////////////////// +NTSTATUS LlsrLocalProductInfoSetW( + LLS_HANDLE Handle, + LPWSTR Product, + DWORD Level, + PLLS_SERVER_PRODUCT_INFOW BufPtr + ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ + +{ +#if DBG + if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC)) + dprintf(TEXT("LLS TRACE: LlsLocalProductInfoSetW\n")); +#endif + + return STATUS_NOT_SUPPORTED; +} // LlsrLocalProductInfoSetW + + +///////////////////////////////////////////////////////////////////////// +NTSTATUS LlsrLocalProductInfoSetA( + LLS_HANDLE Handle, + LPSTR Product, + DWORD Level, + PLLS_SERVER_PRODUCT_INFOA BufPtr + ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ + +{ +#if DBG + if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC)) + dprintf(TEXT("LLS TRACE: LlsLocalProductInfoSetA\n")); +#endif + + return STATUS_NOT_SUPPORTED; +} // LlsrLocalProductInfoSetA + + +///////////////////////////////////////////////////////////////////////// +NTSTATUS LlsrServiceInfoGetW( + LLS_HANDLE Handle, + DWORD Level, + PLLS_SERVICE_INFOW *BufPtr + ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + NTSTATUS Status = STATUS_SUCCESS; + PLLS_SERVICE_INFO_0 pInfo; + FILETIME ftTimeStartedLocal; + LARGE_INTEGER llTimeStartedLocal; + LARGE_INTEGER llTimeStartedSystem; + + +#if DBG + if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC)) + dprintf(TEXT("LLS TRACE: LlsServiceInfoGetW\n")); +#endif + + if (Level != 0) + return STATUS_INVALID_LEVEL; + + if (BufPtr == NULL) + return STATUS_INVALID_PARAMETER; + + pInfo = (PLLS_SERVICE_INFO_0) MIDL_user_allocate(sizeof(LLS_SERVICE_INFO_0)); + if (pInfo == NULL) + return STATUS_NO_MEMORY; + + // make sure the config info is up-to-date + ConfigInfoRegistryUpdate(); + + RtlEnterCriticalSection(&ConfigInfoLock); + + pInfo->Version = ConfigInfo.Version; + pInfo->Mode = LLS_MODE_ENTERPRISE_SERVER; + pInfo->ReplicateTo = ConfigInfo.ReplicateTo; + pInfo->EnterpriseServer = ConfigInfo.EnterpriseServer; + pInfo->ReplicationType = ConfigInfo.ReplicationType; + pInfo->ReplicationTime = ConfigInfo.ReplicationTime; + pInfo->LastReplicated = ConfigInfo.LastReplicatedSeconds; + pInfo->UseEnterprise = ConfigInfo.UseEnterprise; + + SystemTimeToFileTime( &ConfigInfo.Started, &ftTimeStartedLocal ); + + RtlLeaveCriticalSection(&ConfigInfoLock); + + // convert time started (a local SYSTEMTIME) to a system time DWORD in seconds since 1980 + llTimeStartedLocal.u.LowPart = ftTimeStartedLocal.dwLowDateTime; + llTimeStartedLocal.u.HighPart = ftTimeStartedLocal.dwHighDateTime; + + RtlLocalTimeToSystemTime( &llTimeStartedLocal, &llTimeStartedSystem ); + RtlTimeToSecondsSince1980( &llTimeStartedSystem, &pInfo->TimeStarted ); + + *BufPtr = (PLLS_SERVICE_INFOW) pInfo; + + return STATUS_SUCCESS; + +} // LlsrServiceInfoGetW + + +///////////////////////////////////////////////////////////////////////// +NTSTATUS LlsrServiceInfoGetA( + LLS_HANDLE Handle, + DWORD Level, + PLLS_SERVICE_INFOA *BufPtr + ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ +#if DBG + if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC)) + dprintf(TEXT("LLS TRACE: LlsServiceInfoGetA\n")); +#endif + + return STATUS_NOT_SUPPORTED; +} // LlsrServiceInfoGetA + + +///////////////////////////////////////////////////////////////////////// +NTSTATUS LlsrServiceInfoSetW( + LLS_HANDLE Handle, + DWORD Level, + PLLS_SERVICE_INFOW BufPtr + ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ +#if DBG + if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC)) + dprintf(TEXT("LLS TRACE: LlsServiceInfoSetW\n")); +#endif + + return STATUS_NOT_SUPPORTED; +} // LlsrServiceInfoSetW + + +///////////////////////////////////////////////////////////////////////// +NTSTATUS LlsrServiceInfoSetA( + LLS_HANDLE Handle, + DWORD Level, + PLLS_SERVICE_INFOA BufPtr + ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ +#if DBG + if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC)) + dprintf(TEXT("LLS TRACE: LlsServiceInfoSetA\n")); +#endif + + return STATUS_NOT_SUPPORTED; +} // LlsrServiceInfoSetA + + +///////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////// +// Replication Functions + +///////////////////////////////////////////////////////////////////////// +VOID __RPC_USER LLS_REPL_HANDLE_rundown( + LLS_REPL_HANDLE Handle + ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ + +{ + REPL_CONTEXT_TYPE *pClient; + LLS_REPL_HANDLE xHandle; + +#if DBG + if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC)) + dprintf(TEXT("LLS TRACE: LLS_REPL_HANDLE_rundown\n")); +#endif + + if (Handle == NULL) + return; + + pClient = (REPL_CONTEXT_TYPE *) Handle; + + if (pClient != NULL) + if (pClient->Active) { + xHandle = Handle; + LlsrReplClose(&xHandle); + } + + MIDL_user_free(pClient); + +} // LLS_REPL_HANDLE_rundown + + +///////////////////////////////////////////////////////////////////////// +NTSTATUS LlsrReplConnect( + PLLS_REPL_HANDLE Handle, + LPTSTR Name + ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ + +{ + REPL_CONTEXT_TYPE *pClient; + +#if DBG + if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC)) + dprintf(TEXT("LLS TRACE: LlsReplConnect: %s\n"), Name); +#endif + + pClient = (REPL_CONTEXT_TYPE *) midl_user_allocate(sizeof(REPL_CONTEXT_TYPE)); + ASSERT(pClient != NULL); + + if (Name != NULL) + lstrcpy(pClient->Name, Name); + else + lstrcpy(pClient->Name, TEXT("")); + + pClient->Active = TRUE; + pClient->Replicated = FALSE; + + pClient->ServicesSent = FALSE; + pClient->ServiceTableSize = 0; + pClient->Services = NULL; + + pClient->ServersSent = FALSE; + pClient->ServerTableSize = 0; + pClient->Servers = NULL; + + pClient->ServerServicesSent = FALSE; + pClient->ServerServiceTableSize = 0; + pClient->ServerServices = NULL; + + pClient->UsersSent = FALSE; + pClient->UserLevel = 0; + pClient->UserTableSize = 0; + pClient->Users = NULL; + + pClient->CertDbSent = FALSE; + pClient->CertDbProductStringSize = 0; + pClient->CertDbProductStrings = NULL; + pClient->CertDbNumHeaders = 0; + pClient->CertDbHeaders = NULL; + pClient->CertDbNumClaims = 0; + pClient->CertDbClaims = NULL; + + pClient->ProductSecuritySent = FALSE; + pClient->ProductSecurityStringSize = 0; + pClient->ProductSecurityStrings = NULL; + + *Handle = pClient; + + return STATUS_SUCCESS; +} // LlsrReplConnect + + +///////////////////////////////////////////////////////////////////////// +NTSTATUS LlsrReplClose( + LLS_REPL_HANDLE *pHandle + ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ + +{ + NTSTATUS Status; + BOOL Replicated = TRUE; + LLS_REPL_HANDLE Handle = NULL; + TCHAR ComputerName[MAX_COMPUTERNAME_LENGTH + 1]; + REPL_CONTEXT_TYPE *pClient; + PSERVER_RECORD Server; + ULONG i; + +#if DBG + if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC)) + dprintf(TEXT("LLS TRACE: LlsReplClose\n")); +#endif + + ASSERT (pHandle != NULL); + + Handle = *pHandle; + pClient = (REPL_CONTEXT_TYPE *) Handle; + pClient->Active = FALSE; + + // + // Check to see if we have all the information from the client - if we do + // then munge this information into our internal tables. + // + if (pClient->ServersSent && pClient->UsersSent && pClient->ServicesSent && pClient->ServerServicesSent) { +#if DBG + if (TraceFlags & TRACE_RPC) + dprintf(TEXT("LLS Replication - Munging Data\n")); +#endif + + UnpackAll ( + pClient->ServiceTableSize, + pClient->Services, + pClient->ServerTableSize, + pClient->Servers, + pClient->ServerServiceTableSize, + pClient->ServerServices, + pClient->UserLevel, + pClient->UserTableSize, + pClient->Users + ); + + if ( pClient->CertDbSent ) + { + CertDbUnpack( + pClient->CertDbProductStringSize, + pClient->CertDbProductStrings, + pClient->CertDbNumHeaders, + pClient->CertDbHeaders, + pClient->CertDbNumClaims, + pClient->CertDbClaims, + TRUE ); + } + + if ( pClient->ProductSecuritySent ) + { + ProductSecurityUnpack( + pClient->ProductSecurityStringSize, + pClient->ProductSecurityStrings ); + } + } else + Replicated = FALSE; + + ////////////////////////////////////////////////////////////////// + // + // Replication Finished - clean up the context data. + // +#if DBG + if (TraceFlags & TRACE_RPC) + dprintf(TEXT("LLS Replication - Munging Finished\n")); +#endif + + if (pClient->Servers != NULL) { + for (i = 0; i < pClient->ServerTableSize; i++) + MIDL_user_free(pClient->Servers[i].Name); + + MIDL_user_free(pClient->Servers); + } + + if (pClient->Services != NULL) { + for (i = 0; i < pClient->ServiceTableSize; i++) { + MIDL_user_free(pClient->Services[i].Name); + MIDL_user_free(pClient->Services[i].FamilyName); + } + + MIDL_user_free(pClient->Services); + } + + if (pClient->ServerServices != NULL) + MIDL_user_free(pClient->ServerServices); + + if (pClient->Users != NULL) { + for (i = 0; i < pClient->UserTableSize; i++) + { + if ( 0 == pClient->UserLevel ) + { + MIDL_user_free( ((PREPL_USER_RECORD_0) (pClient->Users))[i].Name ); + } + else + { + MIDL_user_free( ((PREPL_USER_RECORD_1) (pClient->Users))[i].Name ); + } + } + + MIDL_user_free(pClient->Users); + } + + if (pClient->CertDbProductStrings != NULL) + { + MIDL_user_free(pClient->CertDbProductStrings); + } + + if (pClient->CertDbHeaders != NULL) + { + MIDL_user_free(pClient->CertDbHeaders); + } + + if (pClient->CertDbClaims != NULL) + { + MIDL_user_free(pClient->CertDbClaims); + } + + if (pClient->ProductSecurityStrings != NULL) + { + MIDL_user_free(pClient->ProductSecurityStrings); + } + + if (pClient->Replicated) { + if (Replicated) { + RtlAcquireResourceShared(&ServerListLock, TRUE); + Server = ServerListFind(pClient->Name); + RtlReleaseResource(&ServerListLock); + + ASSERT(Server != NULL); + if (Server != NULL) + Server->LastReplicated = pClient->ReplicationStart; + } + + RtlEnterCriticalSection(&ConfigInfoLock); + i = --ConfigInfo.NumReplicating; + RtlLeaveCriticalSection(&ConfigInfoLock); + + if ( !i ) + { + // no one's replicating; save all our data files + SaveAll(); + } + } + + return STATUS_SUCCESS; +} // LlsrReplClose + + +///////////////////////////////////////////////////////////////////////// +NTSTATUS +LlsrReplicationRequestW( + LLS_HANDLE Handle, + DWORD Version, + PREPL_REQUEST pRequest + ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + TCHAR ComputerName[MAX_COMPUTERNAME_LENGTH + 1]; + REPL_CONTEXT_TYPE *pClient; + PSERVER_RECORD Server; + +#if DBG + if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC | TRACE_REPLICATION)) + dprintf(TEXT("LLS TRACE: LlsReplicationRequestW: %s\n"), ((PCLIENT_CONTEXT_TYPE) Handle)->Name); +#endif + + if (Version != REPL_VERSION) { + return STATUS_INVALID_LEVEL; + } + + if (pRequest == NULL) + return STATUS_INVALID_PARAMETER; + + // + // Check Enterprise server date from client to see if we need to update + // ours. Also, send back new one for the client. + // + RtlEnterCriticalSection(&ConfigInfoLock); + lstrcpy(ComputerName, ConfigInfo.ComputerName); + + if (ConfigInfo.EnterpriseServerDate < pRequest->EnterpriseServerDate) { + if (lstrlen(pRequest->EnterpriseServer) != 0) { + lstrcpy(ConfigInfo.EnterpriseServer, pRequest->EnterpriseServer); + ConfigInfo.EnterpriseServerDate = pRequest->EnterpriseServerDate; + } + } + + lstrcpy(pRequest->EnterpriseServer, ConfigInfo.EnterpriseServer); + pRequest->EnterpriseServerDate = ConfigInfo.EnterpriseServerDate; + + // + // Increment Repl Count + // + ConfigInfo.NumReplicating++; + RtlLeaveCriticalSection(&ConfigInfoLock); + + // + // Find this server in our server list (add it if not there) + // + pClient = (REPL_CONTEXT_TYPE *) Handle; + pClient->Replicated = TRUE; + RtlAcquireResourceExclusive(&ServerListLock, TRUE); + Server = ServerListAdd(pClient->Name, ComputerName); + RtlReleaseResource(&ServerListLock); + + if (Server == NULL) { + ASSERT(FALSE); + return STATUS_NO_MEMORY; + } + + pClient->ReplicationStart = pRequest->CurrentTime; + pRequest->LastReplicated = Server->LastReplicated; + return STATUS_SUCCESS; +} // LlsrReplicationRequestW + + +///////////////////////////////////////////////////////////////////////// +NTSTATUS +LlsrReplicationServerAddW( + LLS_HANDLE Handle, + ULONG NumRecords, + PREPL_SERVER_RECORD Servers + ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + REPL_CONTEXT_TYPE *pClient; + +#if DBG + if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC)) + dprintf(TEXT("LLS TRACE: LlsReplicationServerAddW\n")); +#endif + + pClient = (REPL_CONTEXT_TYPE *) Handle; + + pClient->ServersSent = TRUE; + pClient->ServerTableSize = NumRecords; + pClient->Servers = Servers; + + return STATUS_SUCCESS; +} // LlsrReplicationServerAddW + + +///////////////////////////////////////////////////////////////////////// +NTSTATUS +LlsrReplicationServerServiceAddW( + LLS_HANDLE Handle, + ULONG NumRecords, + PREPL_SERVER_SERVICE_RECORD ServerServices + ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + REPL_CONTEXT_TYPE *pClient; + +#if DBG + if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC)) + dprintf(TEXT("LLS TRACE: LlsReplicationServerServiceAddW\n")); +#endif + + pClient = (REPL_CONTEXT_TYPE *) Handle; + + pClient->ServerServicesSent = TRUE; + pClient->ServerServiceTableSize = NumRecords; + pClient->ServerServices = ServerServices; + + return STATUS_SUCCESS; +} // LlsrReplicationServerServiceAddW + + +///////////////////////////////////////////////////////////////////////// +NTSTATUS +LlsrReplicationServiceAddW( + LLS_HANDLE Handle, + ULONG NumRecords, + PREPL_SERVICE_RECORD Services + ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + REPL_CONTEXT_TYPE *pClient; + +#if DBG + if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC)) + dprintf(TEXT("LLS TRACE: LlsReplicationServiceAddW\n")); +#endif + + pClient = (REPL_CONTEXT_TYPE *) Handle; + + pClient->ServicesSent = TRUE; + pClient->ServiceTableSize = NumRecords; + pClient->Services = Services; + + return STATUS_SUCCESS; +} // LlsrReplicationServiceAddW + + +///////////////////////////////////////////////////////////////////////// +NTSTATUS +LlsrReplicationUserAddW( + LLS_HANDLE Handle, + ULONG NumRecords, + PREPL_USER_RECORD_0 Users + ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + REPL_CONTEXT_TYPE *pClient; + +#if DBG + if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC)) + dprintf(TEXT("LLS TRACE: LlsReplicationUserAddW\n")); +#endif + + pClient = (REPL_CONTEXT_TYPE *) Handle; + + pClient->UsersSent = TRUE; + pClient->UserLevel = 0; + pClient->UserTableSize = NumRecords; + pClient->Users = Users; + + return STATUS_SUCCESS; +} // LlsrReplicationUserAddW + + + +///////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////// +// Licensing Functions + +///////////////////////////////////////////////////////////////////////// +NTSTATUS +LlsrLicenseRequestW( + LPDWORD LicenseHandle, + LPWSTR ProductID, + ULONG VersionIndex, + BOOLEAN IsAdmin, + ULONG DataType, + ULONG DataSize, + PBYTE Data + ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + NTSTATUS Status; + ULONG Handle = 0xFFFFFFFFL; + +#if DBG + if ( TraceFlags & (TRACE_FUNCTION_TRACE) ) + dprintf(TEXT("LLS TRACE: LlsrLicenseRequestW\n")); +#endif + + Status = DispatchRequestLicense(DataType, Data, ProductID, VersionIndex, IsAdmin, &Handle); + *LicenseHandle = Handle; + + return Status; +} // LlsrLicenseRequestW + + +NTSTATUS +LlsrLicenseFree( + DWORD LicenseHandle + ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ +#if DBG + if ( TraceFlags & (TRACE_FUNCTION_TRACE) ) + dprintf(TEXT("LLS TRACE: LlsrLicenseFree\n")); +#endif + + + DispatchFreeLicense(LicenseHandle); + return STATUS_SUCCESS; +} // LlsrLicenseFree + + + + +///////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////// + + +#if DBG + +///////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////// +// Debugging API's + +///////////////////////////////////////////////////////////////////////// +NTSTATUS +LlsrDbgTableDump( + DWORD Table + ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + // + // FreeHandle is actually TableID + // + switch(Table) { + case SERVICE_TABLE_NUM: + ServiceListDebugDump(); + break; + + case USER_TABLE_NUM: + UserListDebugDump(); + break; + + case SID_TABLE_NUM: + SidListDebugDump(); + break; + + case LICENSE_TABLE_NUM: + LicenseListDebugDump(); + break; + + case ADD_CACHE_TABLE_NUM: + AddCacheDebugDump(); + break; + + case MASTER_SERVICE_TABLE_NUM: + MasterServiceListDebugDump(); + break; + + case SERVICE_FAMILY_TABLE_NUM: + MasterServiceRootDebugDump(); + break; + + case MAPPING_TABLE_NUM: + MappingListDebugDump(); + break; + + case SERVER_TABLE_NUM: + ServerListDebugDump(); + break; + + case SECURE_PRODUCT_TABLE_NUM: + ProductSecurityListDebugDump(); + break; + + case CERTIFICATE_TABLE_NUM: + CertDbDebugDump(); + break; + } + + return STATUS_SUCCESS; +} // LlsrDbgTableDump + + +///////////////////////////////////////////////////////////////////////// +NTSTATUS +LlsrDbgTableInfoDump( + DWORD Table, + LPTSTR Item + ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + switch(Table) { + case SERVICE_TABLE_NUM: + ServiceListDebugInfoDump((PVOID) Item); + break; + + case USER_TABLE_NUM: + UserListDebugInfoDump((PVOID) Item); + break; + +// case SID_TABLE_NUM: +// SidListDebugInfoDump((PVOID) Item); +// break; + +// case LICENSE_TABLE_NUM: +// LicenseListInfoDebugDump((PVOID) Item); +// break; + +// case ADD_CACHE_TABLE_NUM: +// AddCacheDebugDump((PVOID) Item); +// break; + + case MASTER_SERVICE_TABLE_NUM: + MasterServiceListDebugInfoDump((PVOID) Item); + break; + + case SERVICE_FAMILY_TABLE_NUM: + MasterServiceRootDebugInfoDump((PVOID) Item); + break; + + case MAPPING_TABLE_NUM: + MappingListDebugInfoDump((PVOID) Item); + break; + + case SERVER_TABLE_NUM: + ServerListDebugInfoDump((PVOID) Item); + break; + } + + return STATUS_SUCCESS; +} // LlsrDbgTableInfoDump + + +///////////////////////////////////////////////////////////////////////// +NTSTATUS +LlsrDbgTableFlush( + DWORD Table + ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + return STATUS_SUCCESS; +} // LlsrDbgTableFlush + + +///////////////////////////////////////////////////////////////////////// +NTSTATUS +LlsrDbgTraceSet( + DWORD Flags + ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + TraceFlags = Flags; + return STATUS_SUCCESS; +} // LlsrDbgTraceSet + + +///////////////////////////////////////////////////////////////////////// +NTSTATUS +LlsrDbgConfigDump( + ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + ConfigInfoDebugDump(); + return STATUS_SUCCESS; +} // LlsrDbgConfigDump + + +///////////////////////////////////////////////////////////////////////// +NTSTATUS +LlsrDbgReplicationForce( + ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + NtSetEvent( ReplicationEvent, NULL ); + return STATUS_SUCCESS; +} // LlsrDbgReplicationForce + + +///////////////////////////////////////////////////////////////////////// +NTSTATUS +LlsrDbgReplicationDeny( + ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + return STATUS_SUCCESS; +} // LlsrDbgReplicationDeny + + +///////////////////////////////////////////////////////////////////////// +NTSTATUS +LlsrDbgRegistryUpdateForce( + ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + ServiceListResynch(); + return STATUS_SUCCESS; +} // LlsrDbgRegistryUpdateForce + + +///////////////////////////////////////////////////////////////////////// +NTSTATUS +LlsrDbgDatabaseFlush( + ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ +{ + LLSDataSave(); + return STATUS_SUCCESS; +} // LlsrDbgDatabaseFlush + + + +#endif // #if DBG + + +///////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////// +// Extended RPC + +NTSTATUS LlsrProductSecurityGetA( + LLS_HANDLE Handle, + LPSTR Product, + LPBOOL pIsSecure + ) + +/*++ + +Routine Description: + + Retrieve the "security" of a product. A product is deemed secure iff + it requires a secure certificate. In such a case, licenses for the + product may not be entered via the Honesty ("enter the number of + licenses you purchased") method. + + NOTE: Not yet implemented. Use LlsrProductSecurityGetW(). + +Arguments: + + Handle (LLS_HANDLE) + An open LLS handle. + Product (LPSTR) + The name of the product ("DisplayName") for which to receive the + security. + pIsSecure (LPBOOL) + On return, and if successful, indicates whether the product is + secure. + +Return Value: + + STATUS_NOT_SUPPORTED. + +--*/ + +{ +#if DBG + if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC)) + dprintf(TEXT("LLS TRACE: LlsrProductSecurityGetA\n")); +#endif + + return STATUS_NOT_SUPPORTED; +} // LlsrProductSecurityGetA + + +///////////////////////////////////////////////////////////////////////// +NTSTATUS LlsrProductSecurityGetW( + LLS_HANDLE Handle, + LPWSTR DisplayName, + LPBOOL pIsSecure + ) + +/*++ + +Routine Description: + + Retrieve the "security" of a product. A product is deemed secure iff + it requires a secure certificate. In such a case, licenses for the + product may not be entered via the Honesty ("enter the number of + licenses you purchased") method. + +Arguments: + + Handle (LLS_HANDLE) + An open LLS handle. + Product (LPWSTR) + The name of the product ("DisplayName") for which to receive the + security. + pIsSecure (LPBOOL) + On return, and if successful, indicates whether the product is + secure. + +Return Value: + + STATUS_SUCCESS or NTSTATUS error code. + +--*/ + +{ + DWORD i; + +#if DBG + if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC)) + dprintf(TEXT("LLS TRACE: LlsrProductSecurityGetW\n")); +#endif + + RtlAcquireResourceShared( &LocalServiceListLock, TRUE ); + + *pIsSecure = ServiceIsSecure( DisplayName ); + + RtlReleaseResource( &LocalServiceListLock ); + + return STATUS_SUCCESS; +} // LlsrProductSecurityGetW + + +///////////////////////////////////////////////////////////////////////// +NTSTATUS LlsrProductSecuritySetA( + LLS_HANDLE Handle, + LPSTR Product + ) + +/*++ + +Routine Description: + + Flags the given product as secure. A product is deemed secure iff + it requires a secure certificate. In such a case, licenses for the + product may not be entered via the Honesty ("enter the number of + licenses you purchased") method. + + This designation is not reversible and is propagated up the + replication tree. + + NOTE: Not yet implemented. Use LlsrProductSecuritySetW(). + +Arguments: + + Handle (LLS_HANDLE) + An open LLS handle. + Product (LPSTR) + The name of the product ("DisplayName") for which to activate + security. + +Return Value: + + STATUS_NOT_SUPPORTED. + +--*/ + +{ +#if DBG + if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC)) + dprintf(TEXT("LLS TRACE: LlsrProductSecuritySetA\n")); +#endif + + return STATUS_NOT_SUPPORTED; +} // LlsrProductSecuritySetA + + +///////////////////////////////////////////////////////////////////////// +NTSTATUS LlsrProductSecuritySetW( + LLS_HANDLE Handle, + LPWSTR DisplayName + ) + +/*++ + +Routine Description: + + Flags the given product as secure. A product is deemed secure iff + it requires a secure certificate. In such a case, licenses for the + product may not be entered via the Honesty ("enter the number of + licenses you purchased") method. + + This designation is not reversible and is propagated up the + replication tree. + +Arguments: + + Handle (LLS_HANDLE) + An open LLS handle. + Product (LPWSTR) + The name of the product ("DisplayName") for which to activate + security. + +Return Value: + + STATUS_SUCCESS or NTSTATUS error code. + +--*/ + +{ + NTSTATUS nt; + +#if DBG + if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC)) + dprintf(TEXT("LLS TRACE: LlsrProductSecuritySetW\n")); +#endif + + nt = ServiceSecuritySet( DisplayName ); + + return nt; +} // LlsrProductSecuritySetW + + +///////////////////////////////////////////////////////////////////////// +NTSTATUS +LlsrProductLicensesGetA( + LLS_HANDLE Handle, + LPSTR DisplayName, + DWORD Mode, + LPDWORD pQuantity ) + +/*++ + +Routine Description: + + Returns the number of licenses installed for use in the given mode. + + NOTE: Not yet implemented. Use LlsrProductLicensesGetW(). + +Arguments: + + Handle (LLS_HANDLE) + An open LLS handle. + Product (LPSTR) + The name of the product for which to tally licenses. + Mode (DWORD) + Mode for which to tally licenses. + pQuantity (LPDWORD) + On return (and if successful), holds the total number of licenses + for use by the given product in the given license mode. + +Return Value: + + STATUS_NOT_SUPPORTED. + +--*/ + +{ +#if DBG + if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC)) + dprintf(TEXT("LLS TRACE: LlsProductLicensesGetA\n")); +#endif + + return STATUS_NOT_SUPPORTED; +} // LlsProductLicensesGetA + + +///////////////////////////////////////////////////////////////////////// +NTSTATUS +LlsrProductLicensesGetW( + LLS_HANDLE Handle, + LPWSTR DisplayName, + DWORD Mode, + LPDWORD pQuantity ) + +/*++ + +Routine Description: + + Returns the number of licenses installed for use in the given mode. + +Arguments: + + Handle (LLS_HANDLE) + An open LLS handle. + Product (LPWSTR) + The name of the product for which to tally licenses. + Mode (DWORD) + Mode for which to tally licenses. + pQuantity (LPDWORD) + On return (and if successful), holds the total number of licenses + for use by the given product in the given license mode. + +Return Value: + + STATUS_SUCCESS or NTSTATUS error code. + +--*/ + +{ +#if DBG + if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC)) + dprintf(TEXT("LLS TRACE: LlsProductLicensesGetW\n")); +#endif + + *pQuantity = 0; + + if ( !Mode || ServiceIsSecure( DisplayName ) ) + { + // get limit from purchase list + *pQuantity = ProductLicensesGet( DisplayName, Mode ); + } + else + { + DWORD i; + + LocalServiceListUpdate(); + LocalServerServiceListUpdate(); + ServiceListResynch(); + + RtlAcquireResourceShared( &LocalServiceListLock, TRUE ); + + // get limit from concurrent limit setting from the registry + for ( i=0; i < LocalServiceListSize; i++ ) + { + if ( !lstrcmpi( LocalServiceList[i]->DisplayName, DisplayName ) ) + { + // get concurrent limit straight from the registry, not from LocalServiceList! + // (if the mode is set to per seat, the per server licenses in the + // LocalServiceList will always be 0!) + TCHAR szKeyName[ 512 ]; + HKEY hKeyService; + DWORD dwSize; + DWORD dwType; + + wsprintf( szKeyName, TEXT("System\\CurrentControlSet\\Services\\LicenseInfo\\%s"), LocalServiceList[i]->Name ); + + // if error encountered, return STATUS_SUCCESS with *pQuantity = 0 + if ( STATUS_SUCCESS == RegOpenKeyEx( HKEY_LOCAL_MACHINE, szKeyName, 0, KEY_READ, &hKeyService ) ) + { + dwSize = sizeof( *pQuantity ); + RegQueryValueEx( hKeyService, TEXT("ConcurrentLimit"), NULL, &dwType, (LPBYTE) pQuantity, &dwSize ); + + RegCloseKey( hKeyService ); + } + + break; + } + } + + RtlReleaseResource( &LocalServiceListLock ); + } + + return STATUS_SUCCESS; +} // LlsProductLicensesGetW + + +///////////////////////////////////////////////////////////////////////// +NTSTATUS +LlsrCertificateClaimEnumA( + LLS_HANDLE Handle, + DWORD LicenseLevel, + PLLS_LICENSE_INFOA LicensePtr, + PLLS_CERTIFICATE_CLAIM_ENUM_STRUCTA TargetInfo ) + +/*++ + +Routine Description: + + Enumerates the servers on which a given secure certificate is installed. + This function is normally invoked when an attempt to add licenses from + a certificate is denied. + + NOTE: Not yet implemented. Use LlsrCertificateClaimEnumW(). + +Arguments: + + Handle (LLS_HANDLE) + An open LLS handle. + LicenseLevel (DWORD) + The level of the license structure pointed to by pLicenseInfo. + LicensePtr (PLLS_LICENSE_INFOA) + Describes a license for which the certificate targets are requested. + TargetInfo (PLLS_CERTIFICATE_CLAIM_ENUM_STRUCTA) + Container in which to return the target information. + +Return Value: + + STATUS_NOT_SUPPORTED. + +--*/ + +{ +#if DBG + if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC)) + dprintf(TEXT("LLS TRACE: LlsrCertificateClaimEnumA\n")); +#endif + + return STATUS_NOT_SUPPORTED; +} // LlsrCertificateClaimEnumA + + +///////////////////////////////////////////////////////////////////////// +NTSTATUS +LlsrCertificateClaimEnumW( + LLS_HANDLE Handle, + DWORD LicenseLevel, + PLLS_LICENSE_INFOW LicensePtr, + PLLS_CERTIFICATE_CLAIM_ENUM_STRUCTW TargetInfo ) + +/*++ + +Routine Description: + + Enumerates the servers on which a given secure certificate is installed. + This function is normally invoked when an attempt to add licenses from + a certificate is denied. + +Arguments: + + Handle (LLS_HANDLE) + An open LLS handle. + LicenseLevel (DWORD) + The level of the license structure pointed to by pLicenseInfo. + LicensePtr (PLLS_LICENSE_INFOW) + Describes a license for which the certificate targets are requested. + TargetInfo (PLLS_CERTIFICATE_CLAIM_ENUM_STRUCTA) + Container in which to return the target information. + +Return Value: + + STATUS_SUCCESS or NTSTATUS error code. + +--*/ + +{ + NTSTATUS nt; + +#if DBG + if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC)) + dprintf(TEXT("LLS TRACE: LlsrCertificateClaimEnumW\n")); +#endif + + if ( ( 1 != LicenseLevel ) || ( 0 != TargetInfo->Level ) ) + { + nt = STATUS_INVALID_LEVEL; + } + else + { + nt = CertDbClaimsGet( (PLLS_LICENSE_INFO_1) &LicensePtr->LicenseInfo1, + &TargetInfo->LlsCertificateClaimInfo.Level0->EntriesRead, + (PLLS_CERTIFICATE_CLAIM_INFO_0 *) &TargetInfo->LlsCertificateClaimInfo.Level0->Buffer ); + + if ( STATUS_SUCCESS != nt ) + { + TargetInfo->LlsCertificateClaimInfo.Level0->EntriesRead = 0; + TargetInfo->LlsCertificateClaimInfo.Level0->Buffer = NULL; + } + } + + return nt; +} // LlsrCertificateClaimEnumW + + +///////////////////////////////////////////////////////////////////////// +NTSTATUS +LlsrCertificateClaimAddCheckA( + LLS_HANDLE Handle, + DWORD LicenseLevel, + PLLS_LICENSE_INFOA LicensePtr, + LPBOOL pbMayInstall ) + +/*++ + +Routine Description: + + Verify that no more licenses from a given certificate are installed in + a licensing enterprise than are allowed by the certificate. + + NOTE: Not yet implemented. Use LlsrCertificateClaimAddCheckW(). + +Arguments: + + Handle (LLS_HANDLE) + An open LLS handle. + LicenseLevel (DWORD) + The level of the license structure pointed to by pLicenseInfo. + LicensePtr (PLLS_LICENSE_INFOA) + Describes a license for which permission is requested. + pbMayInstall (LPBOOL) + On return (and if successful), indicates whether the certificate + may be legally installed. + +Return Value: + + STATUS_NOT_SUPPORTED. + +--*/ + +{ +#if DBG + if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC)) + dprintf(TEXT("LLS TRACE: LlsrCertificateClaimAddCheckA\n")); +#endif + + return STATUS_NOT_SUPPORTED; +} // LlsrCertificateClaimAddCheckA + + +///////////////////////////////////////////////////////////////////////// +NTSTATUS +LlsrCertificateClaimAddCheckW( + LLS_HANDLE Handle, + DWORD LicenseLevel, + PLLS_LICENSE_INFOW LicensePtr, + LPBOOL pbMayInstall ) + +/*++ + +Routine Description: + + Verify that no more licenses from a given certificate are installed in + a licensing enterprise than are allowed by the certificate. + +Arguments: + + Handle (LLS_HANDLE) + An open LLS handle. + LicenseLevel (DWORD) + The level of the license structure pointed to by pLicenseInfo. + LicensePtr (PLLS_LICENSE_INFOW) + Describes a license for which permission is requested. + pbMayInstall (LPBOOL) + On return (and if successful), indicates whether the certificate + may be legally installed. + +Return Value: + + STATUS_SUCCESS or NTSTATUS error code. + +--*/ + +{ + NTSTATUS nt; + +#if DBG + if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC)) + dprintf(TEXT("LLS TRACE: LlsrCertificateClaimAddCheckW\n")); +#endif + + if ( 1 != LicenseLevel ) + { + nt = STATUS_INVALID_LEVEL; + } + else + { + *pbMayInstall = CertDbClaimApprove( (PLLS_LICENSE_INFO_1) &LicensePtr->LicenseInfo1 ); + nt = STATUS_SUCCESS; + } + + return nt; +} // LlsrCertificateClaimAddCheckW + + +///////////////////////////////////////////////////////////////////////// +NTSTATUS +LlsrCertificateClaimAddA( + LLS_HANDLE Handle, + LPSTR ServerName, + DWORD LicenseLevel, + PLLS_LICENSE_INFOA LicensePtr ) + +/*++ + +Routine Description: + + Declare a number of licenses from a given certificate as being installed + on the target machine. + + NOTE: Not yet implemented. Use LlsCertificateClaimAddW(). + +Arguments: + + Handle (LLS_HANDLE) + An open LLS handle. + ServerName (LPWSTR) + Name of the server on which the licenses are installed. + LicenseLevel (DWORD) + The level of the license structure pointed to by pLicenseInfo. + LicensePtr (PLLS_LICENSE_INFOA) + Describes the installed license. + +Return Value: + + STATUS_NOT_SUPPORTED. + +--*/ + +{ +#if DBG + if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC)) + dprintf(TEXT("LLS TRACE: LlsrCertificateClaimAddA\n")); +#endif + + return STATUS_NOT_SUPPORTED; +} // LlsrCertificateClaimAddA + + +///////////////////////////////////////////////////////////////////////// +NTSTATUS +LlsrCertificateClaimAddW( + LLS_HANDLE Handle, + LPWSTR ServerName, + DWORD LicenseLevel, + PLLS_LICENSE_INFOW LicensePtr ) + +/*++ + +Routine Description: + + Declare a number of licenses from a given certificate as being installed + on the target machine. + +Arguments: + + Handle (LLS_HANDLE) + An open LLS handle to the target license server. + ServerName (LPWSTR) + Name of the server on which the licenses are installed. + LicenseLevel (DWORD) + The level of the license structure pointed to by pLicenseInfo. + LicensePtr (PLLS_LICENSE_INFOW) + Describes the installed license. + +Return Value: + + STATUS_SUCCESS or NTSTATUS error code. + +--*/ + +{ + NTSTATUS nt; + +#if DBG + if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC)) + dprintf(TEXT("LLS TRACE: LlsrCertificateClaimAddW\n")); +#endif + + if ( 1 != LicenseLevel ) + { + nt = STATUS_INVALID_LEVEL; + } + else + { + nt = CertDbClaimEnter( ServerName, (PLLS_LICENSE_INFO_1) &LicensePtr->LicenseInfo1, FALSE, 0 ); + + if ( STATUS_SUCCESS == nt ) + { + nt = CertDbSave(); + } + } + + return nt; +} // LlsrCertificateClaimAddW + + +///////////////////////////////////////////////////////////////////////// +NTSTATUS +LlsrReplicationCertDbAddW( + LLS_REPL_HANDLE Handle, + DWORD Level, + REPL_CERTIFICATES Certificates ) + +/*++ + +Routine Description: + + Called as an optional part of replication, this function receives + the contents of the remote certificate database. + +Arguments: + + Handle (LLS_REPL_HANDLE) + An open replication handle. + Level (DWORD) + Level of replicated certificate information. + Certificates (REPL_CERTIFICATES) + Replicated certificate information. + +Return Value: + + STATUS_SUCCESS or STATUS_INVALID_LEVEL. + +--*/ + +{ + NTSTATUS nt; + REPL_CONTEXT_TYPE * pClient; + +#if DBG + if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC)) + dprintf(TEXT("LLS TRACE: LlsrReplicationCertDbAddW\n")); +#endif + + if ( ( 0 != Level ) + || ( ( NULL != Certificates ) + && ( ( 0 != Certificates->Level0.ClaimLevel ) + || ( 0 != Certificates->Level0.HeaderLevel ) ) ) ) + { + nt = STATUS_INVALID_LEVEL; + } + else + { + pClient = (REPL_CONTEXT_TYPE *) Handle; + + pClient->CertDbSent = TRUE; + + if ( NULL != Certificates ) + { + pClient->CertDbProductStringSize = Certificates->Level0.StringSize; + pClient->CertDbProductStrings = Certificates->Level0.Strings; + pClient->CertDbNumHeaders = Certificates->Level0.HeaderContainer.Level0.NumHeaders; + pClient->CertDbHeaders = Certificates->Level0.HeaderContainer.Level0.Headers; + pClient->CertDbNumClaims = Certificates->Level0.ClaimContainer.Level0.NumClaims; + pClient->CertDbClaims = Certificates->Level0.ClaimContainer.Level0.Claims; + + // free container only + MIDL_user_free( Certificates ); + } + + nt = STATUS_SUCCESS; + } + + return nt; +} // LlsrReplicationCertDbAddW + + +///////////////////////////////////////////////////////////////////////// +NTSTATUS +LlsrReplicationProductSecurityAddW( + LLS_REPL_HANDLE Handle, + DWORD Level, + REPL_SECURE_PRODUCTS SecureProducts ) + +/*++ + +Routine Description: + + Called as an optional part of replication, this function receives + the list of products which require secure certificates. + +Arguments: + + Handle (LLS_REPL_HANDLE) + An open replication handle. + Level (DWORD) + Level of replicated secure product information. + SecureProducts (REPL_SECURE_PRODUCTS) + Replicated secure product information. + +Return Value: + + STATUS_SUCCESS or STATUS_INVALID_LEVEL. + +--*/ + +{ + NTSTATUS nt; + REPL_CONTEXT_TYPE * pClient; + +#if DBG + if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC)) + dprintf(TEXT("LLS TRACE: LlsrReplicationProductSecurityAddW\n")); +#endif + + if ( 0 != Level ) + { + nt = STATUS_INVALID_LEVEL; + } + else + { + pClient = (REPL_CONTEXT_TYPE *) Handle; + + pClient->ProductSecuritySent = TRUE; + + if ( NULL != SecureProducts ) + { + pClient->ProductSecurityStringSize = SecureProducts->Level0.StringSize; + pClient->ProductSecurityStrings = SecureProducts->Level0.Strings; + } + + nt = STATUS_SUCCESS; + } + + return nt; +} // LlsrReplicationProductSecurityAddW + + +///////////////////////////////////////////////////////////////////////// +NTSTATUS +LlsrReplicationUserAddExW( + LLS_REPL_HANDLE Handle, + DWORD Level, + REPL_USERS Users ) + +/*++ + +Routine Description: + + Replacement for LlsrReplicationUserAddW(). (This function, unlike its + counterpart, supports structure levels.) This function replicates the + user list. + +Arguments: + + Handle (LLS_REPL_HANDLE) + An open replication handle. + Level (DWORD) + Level of replicated user information. + Users (REPL_USERS) + Replicated user information. + +Return Value: + + STATUS_SUCCESS or STATUS_INVALID_LEVEL. + +--*/ + +{ + NTSTATUS nt; + REPL_CONTEXT_TYPE * pClient; + +#if DBG + if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC)) + dprintf(TEXT("LLS TRACE: LlsrReplicationUserAddExW\n")); +#endif + + if ( ( 0 != Level ) && ( 1 != Level ) ) + { + nt = STATUS_INVALID_LEVEL; + } + else + { + pClient = (REPL_CONTEXT_TYPE *) Handle; + + pClient->UsersSent = TRUE; + pClient->UserLevel = Level; + + if ( NULL != Users ) + { + if ( 0 == Level ) + { + pClient->UserTableSize = Users->Level0.NumUsers; + pClient->Users = Users->Level0.Users; + } + else + { + pClient->UserTableSize = Users->Level1.NumUsers; + pClient->Users = Users->Level1.Users; + } + + // free container only + MIDL_user_free( Users ); + } + + nt = STATUS_SUCCESS; + } + + return nt; +} // LlsrReplicationUserAddExW + + +NTSTATUS +LlsrCapabilityGet( + LLS_HANDLE Handle, + DWORD cbCapabilities, + LPBYTE pbCapabilities ) +{ + static DWORD adwCapabilitiesSupported[] = + { + LLS_CAPABILITY_SECURE_CERTIFICATES, + LLS_CAPABILITY_REPLICATE_CERT_DB, + LLS_CAPABILITY_REPLICATE_PRODUCT_SECURITY, + LLS_CAPABILITY_REPLICATE_USERS_EX, + LLS_CAPABILITY_SERVICE_INFO_GETW, + LLS_CAPABILITY_LOCAL_SERVICE_API, + (DWORD) -1L + }; + + DWORD i; + DWORD dwCapByte; + DWORD dwCapBit; + + ZeroMemory( pbCapabilities, cbCapabilities ); + + for ( i=0; (DWORD) -1L != adwCapabilitiesSupported[ i ]; i++ ) + { + dwCapByte = adwCapabilitiesSupported[ i ] / 8; + dwCapBit = adwCapabilitiesSupported[ i ] - 8 * dwCapByte; + + if ( dwCapByte < cbCapabilities ) + { + pbCapabilities[ dwCapByte ] |= ( 1 << dwCapBit ); + } + } + + return STATUS_SUCCESS; +} + + +NTSTATUS +LlsrLocalServiceEnumW( + LLS_HANDLE Handle, + PLLS_LOCAL_SERVICE_ENUM_STRUCTW LocalServiceInfo, + DWORD PrefMaxLen, + LPDWORD pTotalEntries, + LPDWORD pResumeHandle ) +{ + NTSTATUS Status = STATUS_SUCCESS; + PVOID BufPtr = NULL; + ULONG BufSize = 0; + ULONG EntriesRead = 0; + ULONG TotalEntries = 0; + ULONG i = 0; + ULONG j = 0; + const DWORD RecordSize = sizeof( LLS_LOCAL_SERVICE_INFO_0W ); + +#if DBG + if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC)) + dprintf(TEXT("LLS TRACE: LlsrLocalServiceEnumW\n")); +#endif + + if ( 0 != LocalServiceInfo->Level ) + { + return STATUS_INVALID_LEVEL; + } + + // Need to scan list so get read access. + RtlAcquireResourceShared(&LocalServiceListLock, TRUE); + + // Calculate how many records will fit into PrefMaxLen buffer. + i = *pResumeHandle; + while ( ( i < LocalServiceListSize ) && ( BufSize < PrefMaxLen ) ) + { + BufSize += RecordSize; + EntriesRead++; + i++; + } + + TotalEntries = EntriesRead; + + // If we overflowed the buffer then back up one record. + if (BufSize > PrefMaxLen) + { + BufSize -= RecordSize; + EntriesRead--; + } + + // Now walk to the end of the list to see how many more records are still + // available. + TotalEntries += LocalServiceListSize - i; + + if (TotalEntries > EntriesRead) + Status = STATUS_MORE_ENTRIES; + + // Reset Enum to correct place. + i = *pResumeHandle; + + // We now know how many records will fit into the buffer, so allocate space + // and fix up pointers so we can copy the information. + BufPtr = MIDL_user_allocate(BufSize); + if (BufPtr == NULL) + { + Status = STATUS_NO_MEMORY; + goto LlsrLocalServiceEnumWExit; + } + + RtlZeroMemory((PVOID) BufPtr, BufSize); + + // Buffers are all setup, so loop through records and copy the data. + while ((j < EntriesRead) && (i < LocalServiceListSize)) + { + ((PLLS_LOCAL_SERVICE_INFO_0W) BufPtr)[j].KeyName = LocalServiceList[i]->Name; + ((PLLS_LOCAL_SERVICE_INFO_0W) BufPtr)[j].DisplayName = LocalServiceList[i]->DisplayName; + ((PLLS_LOCAL_SERVICE_INFO_0W) BufPtr)[j].FamilyDisplayName = LocalServiceList[i]->FamilyDisplayName; + ((PLLS_LOCAL_SERVICE_INFO_0W) BufPtr)[j].Mode = LocalServiceList[i]->Mode; + ((PLLS_LOCAL_SERVICE_INFO_0W) BufPtr)[j].FlipAllow = LocalServiceList[i]->FlipAllow; + ((PLLS_LOCAL_SERVICE_INFO_0W) BufPtr)[j].ConcurrentLimit = LocalServiceList[i]->ConcurrentLimit; + ((PLLS_LOCAL_SERVICE_INFO_0W) BufPtr)[j].HighMark = LocalServiceList[i]->HighMark; + + j++; + i++; + } + +LlsrLocalServiceEnumWExit: +#if DBG + if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC)) + dprintf(TEXT(" TotalEntries: %lu EntriesRead: %lu ResumeHandle: 0x%lX\n"), TotalEntries, EntriesRead, i); +#endif + *pTotalEntries = TotalEntries; + + *pResumeHandle = (ULONG) i; + + LocalServiceInfo->LlsLocalServiceInfo.Level0->EntriesRead = EntriesRead; + LocalServiceInfo->LlsLocalServiceInfo.Level0->Buffer = (PLLS_LOCAL_SERVICE_INFO_0W) BufPtr; + + RtlReleaseResource(&LocalServiceListLock); + return Status; +} + + +NTSTATUS +LlsrLocalServiceEnumA( + LLS_HANDLE Handle, + PLLS_LOCAL_SERVICE_ENUM_STRUCTA LocalServiceInfo, + DWORD PrefMaxLen, + LPDWORD TotalEntries, + LPDWORD ResumeHandle ) +{ +#if DBG + if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC)) + dprintf(TEXT("LLS TRACE: LlsLocalServiceEnumA\n")); +#endif + + return STATUS_NOT_SUPPORTED; +} + + +NTSTATUS +LlsrLocalServiceAddW( + LLS_HANDLE Handle, + DWORD Level, + PLLS_LOCAL_SERVICE_INFOW LocalServiceInfo ) +{ + NTSTATUS Status; + +#if DBG + if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC)) + dprintf(TEXT("LLS TRACE: LlsrLocalServiceAddW\n")); +#endif + + if ( 0 != Level ) + { + Status = STATUS_INVALID_LEVEL; + } + else if ( ( NULL == LocalServiceInfo->LocalServiceInfo0.KeyName ) + || ( NULL == LocalServiceInfo->LocalServiceInfo0.DisplayName ) + || ( NULL == LocalServiceInfo->LocalServiceInfo0.FamilyDisplayName ) ) + { + Status = STATUS_INVALID_PARAMETER; + } + else + { + LONG lError; + HKEY hKeyLicenseInfo; + HKEY hKeyService; + DWORD dwDisposition; + + lError = RegOpenKeyEx( HKEY_LOCAL_MACHINE, REG_KEY_LICENSE, 0, KEY_WRITE, &hKeyLicenseInfo ); + + if ( ERROR_SUCCESS == lError ) + { + // create key + lError = RegCreateKeyEx( hKeyLicenseInfo, LocalServiceInfo->LocalServiceInfo0.KeyName, 0, NULL, 0, KEY_WRITE, NULL, &hKeyService, &dwDisposition ); + + if ( ERROR_SUCCESS == lError ) + { + // set DisplayName + lError = RegSetValueEx( hKeyService, + REG_VALUE_NAME, + 0, + REG_SZ, + (LPBYTE) LocalServiceInfo->LocalServiceInfo0.DisplayName, + ( sizeof( *LocalServiceInfo->LocalServiceInfo0.DisplayName ) + * ( 1 + lstrlen( LocalServiceInfo->LocalServiceInfo0.DisplayName ) ) ) ); + + if ( ERROR_SUCCESS == lError ) + { + // set FamilyDisplayName + lError = RegSetValueEx( hKeyService, + REG_VALUE_FAMILY, + 0, + REG_SZ, + (LPBYTE) LocalServiceInfo->LocalServiceInfo0.FamilyDisplayName, + ( sizeof( *LocalServiceInfo->LocalServiceInfo0.FamilyDisplayName ) + * ( 1 + lstrlen( LocalServiceInfo->LocalServiceInfo0.FamilyDisplayName ) ) ) ); + } + + RegCloseKey( hKeyService ); + } + + RegCloseKey( hKeyLicenseInfo ); + } + + switch ( lError ) + { + case ERROR_SUCCESS: + Status = STATUS_SUCCESS; + break; + case ERROR_FILE_NOT_FOUND: + case ERROR_PATH_NOT_FOUND: + Status = STATUS_OBJECT_NAME_NOT_FOUND; + break; + default: + Status = STATUS_UNSUCCESSFUL; + break; + } + + if ( STATUS_SUCCESS == Status ) + { + // set remaining items and update LocalServiceList + Status = LlsrLocalServiceInfoSetW( Handle, LocalServiceInfo->LocalServiceInfo0.KeyName, Level, LocalServiceInfo ); + } + } + + return Status; +} + + +NTSTATUS +LlsrLocalServiceAddA( + LLS_HANDLE Handle, + DWORD Level, + PLLS_LOCAL_SERVICE_INFOA LocalServiceInfo ) +{ +#if DBG + if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC)) + dprintf(TEXT("LLS TRACE: LlsLocalServiceAddA\n")); +#endif + + return STATUS_NOT_SUPPORTED; +} + + +NTSTATUS +LlsrLocalServiceInfoSetW( + LLS_HANDLE Handle, + LPWSTR KeyName, + DWORD Level, + PLLS_LOCAL_SERVICE_INFOW LocalServiceInfo ) +{ + NTSTATUS Status; + +#if DBG + if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC)) + dprintf(TEXT("LLS TRACE: LlsLocalServiceInfoSetW\n")); +#endif + + if ( 0 != Level ) + { + Status = STATUS_INVALID_LEVEL; + } + else if ( NULL == KeyName ) + { + Status = STATUS_INVALID_PARAMETER; + } + else + { + LONG lError; + HKEY hKeyLicenseInfo; + HKEY hKeyService; + + lError = RegOpenKeyEx( HKEY_LOCAL_MACHINE, REG_KEY_LICENSE, 0, KEY_WRITE, &hKeyLicenseInfo ); + + if ( ERROR_SUCCESS == lError ) + { + lError = RegOpenKeyEx( hKeyLicenseInfo, KeyName, 0, KEY_WRITE, &hKeyService ); + + if ( ERROR_SUCCESS == lError ) + { + // set Mode + lError = RegSetValueEx( hKeyService, REG_VALUE_MODE, 0, REG_DWORD, (LPBYTE) &LocalServiceInfo->LocalServiceInfo0.Mode, sizeof( LocalServiceInfo->LocalServiceInfo0.Mode ) ); + + if ( ERROR_SUCCESS == lError ) + { + // set FlipAllow + lError = RegSetValueEx( hKeyService, REG_VALUE_FLIP, 0, REG_DWORD, (LPBYTE) &LocalServiceInfo->LocalServiceInfo0.FlipAllow, sizeof( LocalServiceInfo->LocalServiceInfo0.FlipAllow ) ); + + if ( ERROR_SUCCESS == lError ) + { + // set ConcurrentLimit + lError = RegSetValueEx( hKeyService, REG_VALUE_LIMIT, 0, REG_DWORD, (LPBYTE) &LocalServiceInfo->LocalServiceInfo0.ConcurrentLimit, sizeof( LocalServiceInfo->LocalServiceInfo0.ConcurrentLimit ) ); + } + } + + RegCloseKey( hKeyService ); + } + + RegCloseKey( hKeyLicenseInfo ); + } + + switch ( lError ) + { + case ERROR_SUCCESS: + Status = STATUS_SUCCESS; + break; + case ERROR_FILE_NOT_FOUND: + case ERROR_PATH_NOT_FOUND: + Status = STATUS_OBJECT_NAME_NOT_FOUND; + break; + default: + Status = STATUS_UNSUCCESSFUL; + break; + } + + if ( STATUS_SUCCESS == Status ) + { + LocalServiceListUpdate(); + LocalServerServiceListUpdate(); + ServiceListResynch(); + } + } + + return Status; +} + + +NTSTATUS +LlsrLocalServiceInfoSetA( + LLS_HANDLE Handle, + LPSTR KeyName, + DWORD Level, + PLLS_LOCAL_SERVICE_INFOA LocalServiceInfo ) +{ +#if DBG + if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC)) + dprintf(TEXT("LLS TRACE: LlsLocalServiceInfoSetA\n")); +#endif + + return STATUS_NOT_SUPPORTED; +} + + +NTSTATUS +LlsrLocalServiceInfoGetW( + LLS_HANDLE Handle, + LPWSTR KeyName, + DWORD Level, + PLLS_LOCAL_SERVICE_INFOW * pLocalServiceInfo ) +{ + NTSTATUS Status; + +#if DBG + if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC)) + dprintf(TEXT("LLS TRACE: LlsrLocalServiceInfoGetW\n")); +#endif + + if ( 0 != Level ) + { + Status = STATUS_INVALID_LEVEL; + } + else if ( NULL == KeyName ) + { + Status = STATUS_INVALID_PARAMETER; + } + else + { + PLOCAL_SERVICE_RECORD pRecord; + + RtlAcquireResourceShared(&LocalServiceListLock, TRUE); + + pRecord = LocalServiceListFind( KeyName ); + + if ( NULL == pRecord ) + { + Status = STATUS_OBJECT_NAME_NOT_FOUND; + } + else + { + *pLocalServiceInfo = MIDL_user_allocate( sizeof( **pLocalServiceInfo ) ); + + if ( NULL == *pLocalServiceInfo ) + { + Status = STATUS_NO_MEMORY; + } + else + { + (*pLocalServiceInfo)->LocalServiceInfo0.KeyName = pRecord->Name; + (*pLocalServiceInfo)->LocalServiceInfo0.DisplayName = pRecord->DisplayName; + (*pLocalServiceInfo)->LocalServiceInfo0.FamilyDisplayName = pRecord->FamilyDisplayName; + (*pLocalServiceInfo)->LocalServiceInfo0.Mode = pRecord->Mode; + (*pLocalServiceInfo)->LocalServiceInfo0.FlipAllow = pRecord->FlipAllow; + (*pLocalServiceInfo)->LocalServiceInfo0.ConcurrentLimit = pRecord->ConcurrentLimit; + (*pLocalServiceInfo)->LocalServiceInfo0.HighMark = pRecord->HighMark; + + Status = STATUS_SUCCESS; + } + } + + RtlReleaseResource(&LocalServiceListLock); + } + + return Status; +} + + +NTSTATUS +LlsrLocalServiceInfoGetA( + LLS_HANDLE Handle, + LPSTR KeyName, + DWORD Level, + PLLS_LOCAL_SERVICE_INFOA * pLocalServiceInfo ) +{ +#if DBG + if (TraceFlags & (TRACE_FUNCTION_TRACE | TRACE_RPC)) + dprintf(TEXT("LLS TRACE: LlsLocalServiceInfoGetA\n")); +#endif + + return STATUS_NOT_SUPPORTED; +} + + diff --git a/private/net/svcdlls/lls/server/scaven.c b/private/net/svcdlls/lls/server/scaven.c new file mode 100644 index 000000000..4c7e7c3b7 --- /dev/null +++ b/private/net/svcdlls/lls/server/scaven.c @@ -0,0 +1,198 @@ +/*++ + +Copyright (c) 1995 Microsoft Corporation + +Module Name: + + scaven.c + +Abstract: + +Author: + + Arthur Hanson (arth) 06-Jan-1995 + +Revision History: + + Jeff Parham (jeffparh) 05-Dec-1995 + o Added periodic logging of certificate agreement violations. + +--*/ + +#include <nt.h> +#include <ntrtl.h> +#include <nturtl.h> +#include <windows.h> + +#include "llsapi.h" +#include "debug.h" +#include "llsutil.h" +#include "llssrv.h" +#include "registry.h" +#include "ntlsapi.h" +#include "mapping.h" +#include "msvctbl.h" +#include "svctbl.h" +#include "purchase.h" +#include "perseat.h" +#include "server.h" +#include "repl.h" +#include "llsevent.h" +#include "llsrpc_s.h" +#include "certdb.h" + +NTSTATUS LLSDataSave(); + + +///////////////////////////////////////////////////////////////////////// +VOID +ScavengerThread ( + IN PVOID ThreadParameter + ) + +/*++ + +Routine Description: + +Arguments: + + ThreadParameter - Indicates how many active threads there currently + are. + +Return Value: + + None. + +--*/ + +{ + ULONG i; + ULONG Count = 0; + + // + // Just wait around forver waiting to service things. + // + while (TRUE) { + // + // Wait 15 minutes before checking things out. + // + Sleep(900000L); + +#if DBG + if (TraceFlags & TRACE_FUNCTION_TRACE) + dprintf(TEXT("LLS TRACE: ScavengerThread waking up\n")); +#endif + // + // Update HighMark for local table + // + LocalServerServiceListHighMarkUpdate(); + + // + // Hmm, lets check replication... + // + ConfigInfoUpdate(); + RtlEnterCriticalSection(&ConfigInfoLock); + if (ConfigInfo.Replicate) { + // + // If we are past replication time then do it + // + if (DateLocalGet() > ConfigInfo.NextReplication) + NtSetEvent( ReplicationEvent, NULL ); + + } + RtlLeaveCriticalSection(&ConfigInfoLock); + + // + // Now update our last used time + // + RtlAcquireResourceExclusive(&UserListAddEnumLock, TRUE); + RtlAcquireResourceExclusive(&UserListLock, TRUE); + LastUsedTime = DateSystemGet(); + RtlReleaseResource(&UserListLock); + RtlReleaseResource(&UserListAddEnumLock); + + // + // Check stuff every 6 hours (4 * 15 minutes) + // + Count++; + if (Count > (6 * 4)) { + // Reset counter + Count = 0; + + // + // Save out the data + // + LLSDataSave(); + + // + // Save HighMark to registry + // + LocalServiceListHighMarkSet(); + + if (IsMaster) { + // + // Check for license compliance + // + RtlAcquireResourceShared(&MasterServiceListLock, TRUE); + + for (i = 0; i < MasterServiceListSize; i++) { + if (MasterServiceList[i]->LicensesUsed > MasterServiceList[i]->Licenses) { + LPWSTR SubString[1]; + + // + // Notify the system + // + SubString[0] = (LPWSTR) MasterServiceList[i]->Name; + + LogEvent(LLS_EVENT_PRODUCT_NO_LICENSE, 1, SubString, ERROR_SUCCESS); + } + } + + RtlReleaseResource(&MasterServiceListLock); + + // log certificate violations + CertDbLogViolations(); + } + } + } + +} // ScavengerThread + + +///////////////////////////////////////////////////////////////////////// +VOID +ScavengerInit( ) + +/*++ + +Routine Description: + + Looks in registry for given service and sets values accordingly. + +Arguments: + +Return Value: + + None. + +--*/ + +{ + HANDLE Thread; + DWORD Ignore; + + // + // Just dispatch our scavenger thread + // + Thread = CreateThread( + NULL, + 0L, + (LPTHREAD_START_ROUTINE) ScavengerThread, + 0L, + 0L, + &Ignore + ); + +} // ScavengerInit + + diff --git a/private/net/svcdlls/lls/server/scaven.h b/private/net/svcdlls/lls/server/scaven.h new file mode 100644 index 000000000..a6a0c14b7 --- /dev/null +++ b/private/net/svcdlls/lls/server/scaven.h @@ -0,0 +1,38 @@ +/*++ + +Copyright (c) 1995 Microsoft Corporation + +Module Name: + + Scaven.h + +Abstract: + + +Author: + + Arthur Hanson (arth) Dec 07, 1994 + +Environment: + +Revision History: + +--*/ + +#ifndef _LLS_SCAVEN_H +#define _LLS_SCAVEN_H + + +#ifdef __cplusplus +extern "C" { +#endif + + +VOID ScavengerInit( ); + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/private/net/svcdlls/lls/server/server.c b/private/net/svcdlls/lls/server/server.c new file mode 100644 index 000000000..459a6e984 --- /dev/null +++ b/private/net/svcdlls/lls/server/server.c @@ -0,0 +1,754 @@ +/*++ + +Copyright (c) 1994 Microsoft Corporation + +Module Name: + + server.c + +Abstract: + + +Author: + + Arthur Hanson (arth) 07-Dec-1994 + +Revision History: + +--*/ + +#include <stdlib.h> +#include <nt.h> +#include <ntrtl.h> +#include <nturtl.h> +#include <windows.h> + +#include "llsapi.h" +#include "debug.h" +#include "llsutil.h" +#include "llssrv.h" +#include "registry.h" +#include "ntlsapi.h" +#include "mapping.h" +#include "msvctbl.h" +#include "svctbl.h" +#include "purchase.h" +#include "perseat.h" +#include "server.h" + +#define NO_LLS_APIS +#include "llsapi.h" + + +///////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////// + +ULONG ServerListSize = 0; +PSERVER_RECORD *ServerList = NULL; +PSERVER_RECORD *ServerTable = NULL; + +RTL_RESOURCE ServerListLock; + + +///////////////////////////////////////////////////////////////////////// +VOID +ServerListInit() + +/*++ + +Routine Description: + +Arguments: + + None. + +Return Value: + + None. + +--*/ + +{ + RtlInitializeResource(&ServerListLock); + + // + // Add ourself as the first server (master server) + // + RtlEnterCriticalSection(&ConfigInfoLock); + ServerListAdd( ConfigInfo.ComputerName, NULL); + RtlLeaveCriticalSection(&ConfigInfoLock); + LocalServerServiceListUpdate(); + +} // ServerListInit + + +///////////////////////////////////////////////////////////////////////// +int __cdecl ServerListCompare(const void *arg1, const void *arg2) { + PSERVER_RECORD Svc1, Svc2; + + Svc1 = (PSERVER_RECORD) *((PSERVER_RECORD *) arg1); + Svc2 = (PSERVER_RECORD) *((PSERVER_RECORD *) arg2); + + return lstrcmpi( Svc1->Name, Svc2->Name ); + +} // ServerListCompare + + +///////////////////////////////////////////////////////////////////////// +int __cdecl ServerServiceListCompare(const void *arg1, const void *arg2) { + PSERVER_SERVICE_RECORD Svc1, Svc2; + + Svc1 = (PSERVER_SERVICE_RECORD) *((PSERVER_SERVICE_RECORD *) arg1); + Svc2 = (PSERVER_SERVICE_RECORD) *((PSERVER_SERVICE_RECORD *) arg2); + + return lstrcmpi( MasterServiceTable[Svc1->Service]->Name, MasterServiceTable[Svc2->Service]->Name ); + +} // ServerServiceListCompare + + +///////////////////////////////////////////////////////////////////////// +PSERVER_SERVICE_RECORD +ServerServiceListFind( + LPTSTR Name, + ULONG ServiceTableSize, + PSERVER_SERVICE_RECORD *ServiceList + ) + +/*++ + +Routine Description: + + Internal routine to actually do binary search on ServerServiceList, this + does not do any locking as we expect the wrapper routine to do this. + The search is a simple binary search. + +Arguments: + + +Return Value: + + Pointer to found service table entry or NULL if not found. + +--*/ + +{ + LONG begin = 0; + LONG end; + LONG cur; + int match; + PMASTER_SERVICE_RECORD Service; + +#if DBG + if (TraceFlags & TRACE_FUNCTION_TRACE) + dprintf(TEXT("LLS TRACE: ServerServiceListFind\n")); +#endif + + if (ServiceTableSize == 0) + return NULL; + + end = (LONG) ServiceTableSize - 1; + + while (end >= begin) { + // go halfway in-between + cur = (begin + end) / 2; + Service = MasterServiceTable[ServiceList[cur]->Service]; + + // compare the two result into match + match = lstrcmpi(Name, Service->Name); + + if (match < 0) + // move new begin + end = cur - 1; + else + begin = cur + 1; + + if (match == 0) + return ServiceList[cur]; + } + + return NULL; + +} // ServerServiceListFind + + +///////////////////////////////////////////////////////////////////////// +PSERVER_RECORD +ServerListFind( + LPTSTR Name + ) + +/*++ + +Routine Description: + + Internal routine to actually do binary search on ServerList, this + does not do any locking as we expect the wrapper routine to do this. + The search is a simple binary search. + +Arguments: + + ServiceName - + +Return Value: + + Pointer to found server table entry or NULL if not found. + +--*/ + +{ + LONG begin = 0; + LONG end = (LONG) ServerListSize - 1; + LONG cur; + int match; + PSERVER_RECORD Server; + +#if DBG + if (TraceFlags & TRACE_FUNCTION_TRACE) + dprintf(TEXT("LLS TRACE: ServerListFind\n")); +#endif + + if ((ServerListSize == 0) || (Name == NULL)) + return NULL; + + while (end >= begin) { + // go halfway in-between + cur = (begin + end) / 2; + Server = ServerList[cur]; + + // compare the two result into match + match = lstrcmpi(Name, Server->Name); + + if (match < 0) + // move new begin + end = cur - 1; + else + begin = cur + 1; + + if (match == 0) + return Server; + } + + return NULL; + +} // ServerListFind + + +///////////////////////////////////////////////////////////////////////// +PSERVER_SERVICE_RECORD +ServerServiceListAdd( + LPTSTR Name, + ULONG ServiceIndex, + PULONG pServiceTableSize, + PSERVER_SERVICE_RECORD **pServiceList + ) + +/*++ + +Routine Description: + + +Arguments: + + ServiceName - + +Return Value: + + Pointer to added service table entry, or NULL if failed. + +--*/ + +{ + LPTSTR NewName; + PSERVER_SERVICE_RECORD Service; + PSERVER_SERVICE_RECORD *ServiceList; + ULONG ServiceListSize; + +#if DBG + if (TraceFlags & TRACE_FUNCTION_TRACE) + dprintf(TEXT("LLS TRACE: ServerServiceListAdd\n")); +#endif + + if ((Name == NULL) || (*Name == TEXT('\0')) || (pServiceTableSize == NULL) || (pServiceList == NULL)) { +#if DBG + dprintf(TEXT("Error LLS: ServerServiceListAdd Bad Parms\n")); +#endif + ASSERT(FALSE); + return NULL; + } + + ServiceListSize = *pServiceTableSize; + ServiceList = *pServiceList; + + // + // Try to find the name + // + Service = ServerServiceListFind(Name, ServiceListSize, ServiceList); + if (Service != NULL) { + Service->Service = ServiceIndex; + return Service; + } + + // + // No record - so create a new one + // + if (ServiceList == NULL) { + ServiceList = (PSERVER_SERVICE_RECORD *) LocalAlloc(LPTR, sizeof(PSERVER_SERVICE_RECORD)); + } else { + ServiceList = (PSERVER_SERVICE_RECORD *) LocalReAlloc(ServiceList, sizeof(PSERVER_SERVICE_RECORD) * (ServiceListSize + 1), LHND); + } + + // + // Make sure we could allocate server table + // + if (ServiceList == NULL) { + ServiceListSize = 0; + ASSERT(FALSE); + goto ServerServiceListAddExit; + } + + // + // Allocate space for Record. + // + Service = (PSERVER_SERVICE_RECORD) LocalAlloc(LPTR, sizeof(SERVER_SERVICE_RECORD)); + if (Service == NULL) { + ASSERT(FALSE); + return NULL; + } + + ServiceList[ServiceListSize] = Service; + + // + // Initialize other stuff + // + Service->Service = ServiceIndex; + Service->MaxSessionCount = 0; + Service->MaxSetSessionCount = 0; + Service->HighMark = 0; + Service->Flags = 0; + + ServiceListSize++; + + // Have added the entry - now need to sort it in order of the service names + qsort((void *) ServiceList, (size_t) ServiceListSize, sizeof(PSERVER_SERVICE_RECORD), ServerServiceListCompare); + +ServerServiceListAddExit: + *pServiceTableSize = ServiceListSize; + *pServiceList = ServiceList; + return Service; + +} // ServerServiceListAdd + + +///////////////////////////////////////////////////////////////////////// +PSERVER_RECORD +ServerListAdd( + LPTSTR Name, + LPTSTR Master + ) + +/*++ + +Routine Description: + + +Arguments: + + ServiceName - + +Return Value: + + Pointer to added service table entry, or NULL if failed. + +--*/ + +{ + LPTSTR NewName; + PSERVER_RECORD Server; + PSERVER_RECORD pMaster; + +#if DBG + if (TraceFlags & TRACE_FUNCTION_TRACE) + dprintf(TEXT("LLS TRACE: ServerListAdd\n")); +#endif + + if ((Name == NULL) || (*Name == TEXT('\0'))) { +#if DBG + dprintf(TEXT("Error LLS: ServerListAdd Bad Parms\n")); +#endif + ASSERT(FALSE); + return NULL; + } + + // + // Try to find the name + // + Server = ServerListFind(Name); + if (Server != NULL) { + return Server; + } + + // + // No record - so create a new one + // + if (ServerList == NULL) { + ServerList = (PSERVER_RECORD *) LocalAlloc(LPTR, sizeof(PSERVER_RECORD)); + ServerTable = (PSERVER_RECORD *) LocalAlloc(LPTR, sizeof(PSERVER_RECORD)); + } else { + ServerList = (PSERVER_RECORD *) LocalReAlloc(ServerList, sizeof(PSERVER_RECORD) * (ServerListSize + 1), LHND); + ServerTable = (PSERVER_RECORD *) LocalReAlloc(ServerTable, sizeof(PSERVER_RECORD) * (ServerListSize + 1), LHND); + } + + // + // Make sure we could allocate server table + // + if ((ServerList == NULL) || (ServerTable == NULL)) { + ASSERT(FALSE); + ServerList = NULL; + ServerTable = NULL; + ServerListSize = 0; + return NULL; + } + + // + // Allocate space for Record. + // + Server = (PSERVER_RECORD) LocalAlloc(LPTR, sizeof(SERVER_RECORD)); + if (Server == NULL) { + ASSERT(FALSE); + return NULL; + } + + ServerList[ServerListSize] = Server; + ServerTable[ServerListSize] = Server; + + NewName = (LPTSTR) LocalAlloc(LPTR, (lstrlen(Name) + 1) * sizeof(TCHAR)); + if (NewName == NULL) { + ASSERT(FALSE); + LocalFree(Server); + return NULL; + } + + // now copy it over... + Server->Name = NewName; + lstrcpy(NewName, Name); + + // + // Initialize other stuff + // + Server->Index = ServerListSize + 1; + Server->LastReplicated = 0; + Server->IsReplicating = FALSE; + + // + // Fixup slave/master chain + // + Server->MasterServer = 0; + Server->NextServer = 0; + if (Master != NULL) { + pMaster = ServerListFind(Master); + + if (pMaster != NULL) { + Server->MasterServer = pMaster->Index; + Server->NextServer = pMaster->SlaveServer; + pMaster->SlaveServer = Server->Index; + } else { + ASSERT(FALSE); + } + } + + Server->SlaveServer = 0; + + Server->ServiceTableSize = 0; + Server->Services = NULL; + + ServerListSize++; + + // Have added the entry - now need to sort it in order of the service names + qsort((void *) ServerList, (size_t) ServerListSize, sizeof(PSERVER_RECORD), ServerListCompare); + + return Server; + +} // ServerListAdd + + +///////////////////////////////////////////////////////////////////////// +VOID +LocalServerServiceListUpdate( + ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ + +{ + PSERVER_RECORD Server; + PMASTER_SERVICE_RECORD Service; + PSERVER_SERVICE_RECORD ServerService; + ULONG i, Index; + +#if DBG + if (TraceFlags & TRACE_FUNCTION_TRACE) + dprintf(TEXT("LLS TRACE: LocalServerServiceListUpdate\n")); +#endif + + // + // Find our local server in the Server table + // + RtlEnterCriticalSection(&ConfigInfoLock); + Server = ServerListFind( ConfigInfo.ComputerName ); + RtlLeaveCriticalSection(&ConfigInfoLock); + + ASSERT(Server != NULL); + if (Server == NULL) + return; + + RtlAcquireResourceShared(&LocalServiceListLock, TRUE); + RtlAcquireResourceShared(&MasterServiceListLock, TRUE); + + for (i = 0; i < LocalServiceListSize; i++) { + Service = MasterServiceListFind(LocalServiceList[i]->DisplayName); + if (Service == NULL) { + RtlConvertSharedToExclusive(&MasterServiceListLock); + Service = MasterServiceListAdd(LocalServiceList[i]->FamilyDisplayName, LocalServiceList[i]->DisplayName, 0); + RtlConvertExclusiveToShared(&MasterServiceListLock); + } + + if (Service != NULL) { + ServerService = ServerServiceListAdd( Service->Name, Service->Index, &Server->ServiceTableSize, &Server->Services ); + + ASSERT(ServerService != NULL); + if (ServerService != NULL) { + // + // Update high mark if needed + // + if ( LocalServiceList[i]->HighMark > ServerService->HighMark ) + { + ServerService->HighMark = LocalServiceList[i]->HighMark; + } + + // + // Subtract any old licenses we might have + // + Service->MaxSessionCount -= ServerService->MaxSessionCount; + + // + // Now update to current Licenses + // + ServerService->MaxSessionCount = LocalServiceList[i]->ConcurrentLimit; + if (LocalServiceList[i]->ConcurrentLimit > ServerService->MaxSetSessionCount) + ServerService->MaxSetSessionCount = LocalServiceList[i]->ConcurrentLimit; + + Service->MaxSessionCount += ServerService->MaxSessionCount; + ServerService->Flags &= ~LLS_FLAG_PRODUCT_PERSEAT; + + if (LocalServiceList[i]->Mode == 0) + ServerService->Flags |= LLS_FLAG_PRODUCT_PERSEAT; + + } + + } + + } + + RtlReleaseResource(&MasterServiceListLock); + RtlReleaseResource(&LocalServiceListLock); + +} // LocalServerServiceListUpdate + + +///////////////////////////////////////////////////////////////////////// +VOID +LocalServerServiceListHighMarkUpdate( + ) + +/*++ + +Routine Description: + + We've got to do this separatly because it locks the Service Table + and it needs to be done in reverse. I.E. We need to run through + the Service Table to get the display names and then look it up in + the ServerServicesList instead of running through the + ServerServicesList. + +Arguments: + + +Return Value: + + +--*/ + +{ + PSERVER_RECORD Server; + PSERVER_SERVICE_RECORD ServerService; + PMASTER_SERVICE_RECORD Service; + ULONG i; + + // + // Find our local server in the Server table + // + RtlEnterCriticalSection(&ConfigInfoLock); + Server = ServerListFind( ConfigInfo.ComputerName ); + RtlLeaveCriticalSection(&ConfigInfoLock); + + ASSERT(Server != NULL); + if (Server == NULL) + return; + + RtlAcquireResourceShared(&MasterServiceListLock, TRUE); + RtlAcquireResourceShared(&ServiceListLock, TRUE); + + for (i = 0; i < ServiceListSize; i++) { + + ServerService = ServerServiceListFind( ServiceList[i]->DisplayName, Server->ServiceTableSize, Server->Services ); + + if (ServerService != NULL) { + Service = MasterServiceListFind(ServiceList[i]->DisplayName); + ASSERT(Service != NULL); + + if (Service != NULL) { + // + // Subtract any old info we might have + // + if (Service->HighMark != 0) + { + Service->HighMark -= ServerService->HighMark; + } + + // + // Now update to current Licenses + // + ServerService->HighMark = ServiceList[i]->HighMark; + Service->HighMark += ServerService->HighMark; + } + } + + } + + RtlReleaseResource(&ServiceListLock); + RtlReleaseResource(&MasterServiceListLock); + +} // LocalServerServiceListHighMarkUpdate + + + +#if DBG + +///////////////////////////////////////////////////////////////////////// +VOID +ServerListDebugDump( ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ + +{ + ULONG i = 0; + + // + // Need to scan list so get read access. + // + RtlAcquireResourceShared(&ServerListLock, TRUE); + + dprintf(TEXT("Server Table, # Entries: %lu\n"), ServerListSize); + if (ServerList == NULL) + goto ServerListDebugDumpExit; + + for (i = 0; i < ServerListSize; i++) { + dprintf(TEXT("%3lu) [%3lu] LR: %s #Svc: %4lu M: %3lu S: %3lu N: %3lu Server: %s\n"), + i + 1, ServerList[i]->Index, TimeToString(ServerList[i]->LastReplicated), ServerList[i]->ServiceTableSize, + ServerList[i]->MasterServer, ServerList[i]->SlaveServer, ServerList[i]->NextServer, ServerList[i]->Name); + } + +ServerListDebugDumpExit: + RtlReleaseResource(&ServerListLock); + + return; +} // ServerListDebugDump + + +///////////////////////////////////////////////////////////////////////// +VOID +ServerListDebugInfoDump( PVOID Data ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ + +{ + ULONG i = 0; + PSERVER_RECORD Server = NULL; + + // + // Need to scan list so get read access. + // + RtlAcquireResourceShared(&ServerListLock, TRUE); + + dprintf(TEXT("Server Table, # Entries: %lu\n"), ServerListSize); + if (ServerList == NULL) + goto ServerListDebugInfoDumpExit; + + if (Data == NULL) + goto ServerListDebugInfoDumpExit; + + Server = ServerListFind( (LPTSTR) Data ); + if (Server == NULL) { + dprintf(TEXT("Server not found: %s\n"), (LPTSTR) Data ); + goto ServerListDebugInfoDumpExit; + } + + // + // Show server + // + dprintf(TEXT("[%3lu] LR: %s #Svc: %4lu M: %3lu S: %3lu N: %3lu Server: %s\n"), + Server->Index, TimeToString(Server->LastReplicated), Server->ServiceTableSize, + Server->MasterServer, Server->SlaveServer, Server->NextServer, Server->Name); + + // + // Now all the services for this server + // + RtlAcquireResourceShared(&MasterServiceListLock, TRUE); + for (i = 0; i < Server->ServiceTableSize; i++) { + dprintf(TEXT(" %3lu) Flags: 0x%4lX MS: %3lu HM: %3lu SHM: %3lu Service: %s\n"), + i + 1, Server->Services[i]->Flags, Server->Services[i]->MaxSessionCount, Server->Services[i]->HighMark, + Server->Services[i]->MaxSetSessionCount, MasterServiceTable[Server->Services[i]->Service]->Name); + + } + RtlReleaseResource(&MasterServiceListLock); + +ServerListDebugInfoDumpExit: + RtlReleaseResource(&ServerListLock); + + return; +} // ServerListDebugInfoDump + +#endif diff --git a/private/net/svcdlls/lls/server/server.h b/private/net/svcdlls/lls/server/server.h new file mode 100644 index 000000000..15dafe202 --- /dev/null +++ b/private/net/svcdlls/lls/server/server.h @@ -0,0 +1,84 @@ +/*++ + +Copyright (c) 1994 Microsoft Corporation + +Module Name: + + Server.h + +Abstract: + + +Author: + + Arthur Hanson (arth) Dec 07, 1994 + +Environment: + +Revision History: + +--*/ + + +#ifndef _LLS_SERVERTBL_H +#define _LLS_SERVERTBL_H + + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct _SERVER_SERVICE_RECORD { + ULONG Service; + DWORD Flags; + + ULONG MaxSessionCount; // Max # simultaneous sessions + ULONG MaxSetSessionCount; // Max # simultaneous sessions ever set + ULONG HighMark; // Max # simultaneous sessions ever attempted +} SERVER_SERVICE_RECORD, *PSERVER_SERVICE_RECORD; + + +typedef struct _SERVER_RECORD { + ULONG Index; + LPTSTR Name; + + DWORD LastReplicated; + BOOL IsReplicating; + + ULONG MasterServer; + ULONG SlaveServer; + ULONG NextServer; + ULONG ServiceTableSize; + PSERVER_SERVICE_RECORD *Services; +} SERVER_RECORD, *PSERVER_RECORD; + + +extern ULONG ServerListSize; +extern PSERVER_RECORD *ServerList; +extern PSERVER_RECORD *ServerTable; + +extern RTL_RESOURCE ServerListLock; + + +VOID ServerListInit(); +PSERVER_RECORD ServerListFind( LPTSTR Name ); +PSERVER_RECORD ServerListAdd( LPTSTR Name, LPTSTR Master ); + +PSERVER_SERVICE_RECORD ServerServiceListFind( LPTSTR Name, ULONG ServiceTableSize, PSERVER_SERVICE_RECORD *ServiceList ); +PSERVER_SERVICE_RECORD ServerServiceListAdd( LPTSTR Name, ULONG ServiceIndex, PULONG pServiceTableSize, PSERVER_SERVICE_RECORD **pServiceList ); +VOID LocalServerServiceListUpdate(); +VOID LocalServerServiceListHighMarkUpdate(); + + +#if DBG + +VOID ServerListDebugDump( ); +VOID ServerListDebugInfoDump( PVOID Data ); +#endif + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/private/net/svcdlls/lls/server/service.c b/private/net/svcdlls/lls/server/service.c new file mode 100644 index 000000000..53c4050e1 --- /dev/null +++ b/private/net/svcdlls/lls/server/service.c @@ -0,0 +1,625 @@ +/*++ + +Copyright (c) 1994 Microsoft Corporation + +Module Name: + + Service.c + +Abstract: + + License Logging Service - Common routines for all service. + +Author: + + Arthur Hanson (arth) Dec 07, 1994 + +Environment: + +Revision History: + +--*/ + +#include <windows.h> +#include <stdio.h> +#include <stdlib.h> +#include <process.h> +#include <tchar.h> +#include <shellapi.h> + +#include "service.h" + + + +// internal variables +SERVICE_STATUS ssStatus; // current status of the service +SERVICE_STATUS_HANDLE sshStatusHandle; +DWORD dwErr = 0; +BOOL bDebug = FALSE; +TCHAR szErr[256]; + +// internal function prototypes +VOID WINAPI ServiceCtrl(DWORD dwCtrlCode); +VOID WINAPI ServiceMain(DWORD dwArgc, LPTSTR *lpszArgv); +VOID CmdInstallService(); +VOID CmdRemoveService(); +VOID CmdDebugService(int argc, char **argv); +BOOL WINAPI ControlHandler ( DWORD dwCtrlType ); +LPTSTR GetLastErrorText( LPTSTR lpszBuf, DWORD dwSize ); + + +///////////////////////////////////////////////////////////////////////// +VOID _CRTAPI1 +main( + int argc, + char **argv + ) +/*++ + +Routine Description: + + Main routine to setup the exception handlers and initialize everything + before spawning threads to listen to LPC and RPC port requests. + + main() either performs the command line task, or calls + StartServiceCtrlDispatcher to register the main service thread. When the + this call returns, the service has stopped, so exit. + +Arguments: + + argc - number of command line arguments + argv - array of command line arguments + +Return Values: + + None. + +--*/ +{ + SERVICE_TABLE_ENTRY dispatchTable[] = { + { TEXT(SZSERVICENAME), (LPSERVICE_MAIN_FUNCTION) ServiceMain }, + { NULL, NULL } + }; + + if ( (argc > 1) && ((*argv[1] == '-') || (*argv[1] == '/')) ) { + if ( _stricmp( "install", argv[1]+1 ) == 0 ) { + CmdInstallService(); + } else if ( _stricmp( "remove", argv[1]+1 ) == 0 ) { + CmdRemoveService(); + } else if ( _stricmp( "debug", argv[1]+1 ) == 0 ) { + bDebug = TRUE; + CmdDebugService(argc, argv); + } else { + goto dispatch; + } + + exit(0); + } + + // if it doesn't match any of the above parameters + // the service control manager may be starting the service + // so we must call StartServiceCtrlDispatcher + dispatch: +#ifdef DEBUG + // this is just to be friendly + printf( "%s -install to install the service\n", SZAPPNAME ); + printf( "%s -remove to remove the service\n", SZAPPNAME ); + printf( "%s -debug <params> to run as a console app for debugging\n", SZAPPNAME ); + printf( "\nStartServiceCtrlDispatcher being called.\n" ); + printf( "This may take several seconds. Please wait.\n" ); +#endif + + if (!StartServiceCtrlDispatcher(dispatchTable)) + MessageLogWrite(TEXT("StartServiceCtrlDispatcher failed.")); + +} // main + + + +///////////////////////////////////////////////////////////////////////// +VOID WINAPI +ServiceMain( + DWORD dwArgc, + LPTSTR *lpszArgv + ) +/*++ + +Routine Description: + + Performs the service initialization and then calls the ServiceStart() + routine to perform majority of the work. + +Arguments: + + dwArgc - number of command line arguments + lpszArgv - array of command line arguments + +Return Values: + + None. + +--*/ +{ + + // register our service control handler: + // + sshStatusHandle = RegisterServiceCtrlHandler( TEXT(SZSERVICENAME), ServiceCtrl); + + if (!sshStatusHandle) + goto cleanup; + + // SERVICE_STATUS members that don't change in example + // + ssStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS; + ssStatus.dwServiceSpecificExitCode = 0; + + + // report the status to the service control manager. + // + if (!ReportStatusToSCMgr( + SERVICE_START_PENDING, // service state + NO_ERROR, // exit code + 3000)) // wait hint + goto cleanup; + + + ServiceStart( dwArgc, lpszArgv ); + +cleanup: + + // try to report the stopped status to the service control manager. + // + if (sshStatusHandle) + (VOID)ReportStatusToSCMgr( + SERVICE_STOPPED, + dwErr, + 0); + + return; +} // ServiceMain + + + +///////////////////////////////////////////////////////////////////////// +VOID WINAPI +ServiceCtrl( + DWORD dwCtrlCode + ) +/*++ + +Routine Description: + + Called by the SCM whenever ControlService() is called on this service. + +Arguments: + + dwCtrlCode - type of control requested + +Return Values: + + None. + +--*/ +{ + DWORD dwState = SERVICE_RUNNING; + + // Handle the requested control code. + // + switch(dwCtrlCode) { + // Stop the service. + // + case SERVICE_CONTROL_STOP: + dwState = SERVICE_STOP_PENDING; + ssStatus.dwCurrentState = SERVICE_STOP_PENDING; + break; + + // Update the service status. + // + case SERVICE_CONTROL_INTERROGATE: + break; + + // invalid control code + // + default: + break; + + } + + ReportStatusToSCMgr(dwState, NO_ERROR, 0); + + if ( SERVICE_CONTROL_STOP == dwCtrlCode ) + { + ServiceStop(); + } +} // ServiceCtrl + + + +///////////////////////////////////////////////////////////////////////// +BOOL +ReportStatusToSCMgr( + DWORD dwCurrentState, + DWORD dwWin32ExitCode, + DWORD dwWaitHint + ) +/*++ + +Routine Description: + + Sets the current status of the service and reports it to the SCM. + +Arguments: + + dwCurrentState - the state of the service + dwWin32ExitCode - error code to report + dwWaitHint - worst case estimate to next checkpoint + +Return Values: + + None. + +--*/ +{ + static DWORD dwCheckPoint = 1; + BOOL fResult = TRUE; + + ssStatus.dwControlsAccepted = 0; + if ( !bDebug ) { // when debugging we don't report to the SCM + if (dwCurrentState == SERVICE_START_PENDING) + ssStatus.dwControlsAccepted = 0; + else + ssStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP; + + ssStatus.dwCurrentState = dwCurrentState; + ssStatus.dwWin32ExitCode = dwWin32ExitCode; + ssStatus.dwWaitHint = dwWaitHint; + + if ( ( dwCurrentState == SERVICE_RUNNING ) || + ( dwCurrentState == SERVICE_STOPPED ) ) + ssStatus.dwCheckPoint = 0; + else + ssStatus.dwCheckPoint = dwCheckPoint++; + + + // Report the status of the service to the service control manager. + // + if (!(fResult = SetServiceStatus( sshStatusHandle, &ssStatus))) { + MessageLogWrite(TEXT("SetServiceStatus")); + } + } + return fResult; +} // ReportStatusToSCMgr + + + +///////////////////////////////////////////////////////////////////////// +VOID +MessageLogWrite( + LPTSTR lpszMsg + ) +/*++ + +Routine Description: + + Logs an error message. + +Arguments: + + lpszMsg - text for message + +Return Values: + + None. + +--*/ +{ + TCHAR szMsg[256]; + HANDLE hEventSource; + LPTSTR lpszStrings[2]; + + + if ( !bDebug ) { + dwErr = GetLastError(); + + // Use event logging to log the error. + // + hEventSource = RegisterEventSource(NULL, TEXT(SZSERVICENAME)); + + _stprintf(szMsg, TEXT("%s error: %d"), TEXT(SZSERVICENAME), dwErr); + lpszStrings[0] = szMsg; + lpszStrings[1] = lpszMsg; + + if (hEventSource != NULL) { + ReportEvent(hEventSource, // handle of event source + EVENTLOG_ERROR_TYPE, // event type + 0, // event category + 0, // event ID + NULL, // current user's SID + 2, // strings in lpszStrings + 0, // no bytes of raw data + lpszStrings, // array of error strings + NULL); // no raw data + + (VOID) DeregisterEventSource(hEventSource); + } + } +} // MessageLogWrite + + + + +///////////////////////////////////////////////////////////////////////// +// +// The following code handles service installation and removal +// +///////////////////////////////////////////////////////////////////////// + + +///////////////////////////////////////////////////////////////////////// +VOID +CmdInstallService() +/*++ + +Routine Description: + + Installs the service. + +Arguments: + + None. + +Return Values: + + None. + +--*/ +{ + SC_HANDLE schService; + SC_HANDLE schSCManager; + + TCHAR szPath[512]; + + if ( GetModuleFileName( NULL, szPath, 512 ) == 0 ) { + _tprintf(TEXT("Unable to install %s - %s\n"), TEXT(SZSERVICEDISPLAYNAME), GetLastErrorText(szErr, 256)); + return; + } + + schSCManager = OpenSCManager( + NULL, // machine (NULL == local) + NULL, // database (NULL == default) + SC_MANAGER_ALL_ACCESS // access required + ); + if ( schSCManager ) { + schService = CreateService( + schSCManager, // SCManager database + TEXT(SZSERVICENAME), // name of service + TEXT(SZSERVICEDISPLAYNAME), // name to display + SERVICE_ALL_ACCESS, // desired access + SERVICE_WIN32_OWN_PROCESS, // service type + SERVICE_DEMAND_START, // start type + SERVICE_ERROR_NORMAL, // error control type + szPath, // service's binary + NULL, // no load ordering group + NULL, // no tag identifier + TEXT(SZDEPENDENCIES), // dependencies + NULL, // LocalSystem account + NULL); // no password + + if ( schService ) { + _tprintf(TEXT("%s installed.\n"), TEXT(SZSERVICEDISPLAYNAME) ); + CloseServiceHandle(schService); + } else { + _tprintf(TEXT("CreateService failed - %s\n"), GetLastErrorText(szErr, 256)); + } + + CloseServiceHandle(schSCManager); + } else + _tprintf(TEXT("OpenSCManager failed - %s\n"), GetLastErrorText(szErr,256)); +} // CmdInstallService + + + +///////////////////////////////////////////////////////////////////////// +VOID +CmdRemoveService() +/*++ + +Routine Description: + + Stops and removes the service. + +Arguments: + + None. + +Return Values: + + None. + +--*/ +{ + SC_HANDLE schService; + SC_HANDLE schSCManager; + + schSCManager = OpenSCManager( + NULL, // machine (NULL == local) + NULL, // database (NULL == default) + SC_MANAGER_ALL_ACCESS // access required + ); + if ( schSCManager ) { + schService = OpenService(schSCManager, TEXT(SZSERVICENAME), SERVICE_ALL_ACCESS); + + if (schService) { + // try to stop the service + if ( ControlService( schService, SERVICE_CONTROL_STOP, &ssStatus ) ) { + _tprintf(TEXT("Stopping %s."), TEXT(SZSERVICEDISPLAYNAME)); + Sleep( 1000 ); + + while( QueryServiceStatus( schService, &ssStatus ) ) { + if ( ssStatus.dwCurrentState == SERVICE_STOP_PENDING ) { + _tprintf(TEXT(".")); + Sleep( 1000 ); + } else + break; + } + + if ( ssStatus.dwCurrentState == SERVICE_STOPPED ) + _tprintf(TEXT("\n%s stopped.\n"), TEXT(SZSERVICEDISPLAYNAME) ); + else + _tprintf(TEXT("\n%s failed to stop.\n"), TEXT(SZSERVICEDISPLAYNAME) ); + + } + + // now remove the service + if( DeleteService(schService) ) + _tprintf(TEXT("%s removed.\n"), TEXT(SZSERVICEDISPLAYNAME) ); + else + _tprintf(TEXT("DeleteService failed - %s\n"), GetLastErrorText(szErr,256)); + + + CloseServiceHandle(schService); + } else + _tprintf(TEXT("OpenService failed - %s\n"), GetLastErrorText(szErr,256)); + + CloseServiceHandle(schSCManager); + } else + _tprintf(TEXT("OpenSCManager failed - %s\n"), GetLastErrorText(szErr,256)); + +} // CmdRemoveService + + + + +///////////////////////////////////////////////////////////////////////// +// +// Routines for running the service as a console app +// +///////////////////////////////////////////////////////////////////////// + + +///////////////////////////////////////////////////////////////////////// +VOID CmdDebugService( + int argc, + char ** argv + ) +/*++ + +Routine Description: + + Runs the service as a console application + +Arguments: + + argc - number of command line arguments + argv - array of command line arguments + +Return Values: + + None. + +--*/ +{ + DWORD dwArgc; + LPTSTR *lpszArgv; + +#ifdef UNICODE + lpszArgv = CommandLineToArgvW(GetCommandLineW(), &(dwArgc) ); +#else + dwArgc = (DWORD) argc; + lpszArgv = argv; +#endif + + _tprintf(TEXT("Debugging %s.\n"), TEXT(SZSERVICEDISPLAYNAME)); + + SetConsoleCtrlHandler( ControlHandler, TRUE ); + + ServiceStart( dwArgc, lpszArgv ); +} // CmdDebugService + + +///////////////////////////////////////////////////////////////////////// +BOOL WINAPI +ControlHandler ( + DWORD dwCtrlType + ) +/*++ + +Routine Description: + + Handle console control events. + +Arguments: + + dwCtrlType - type of control event + lpszMsg - text for message + +Return Values: + + True - handled + False - unhandled + +--*/ +{ + + switch( dwCtrlType ) { + case CTRL_BREAK_EVENT: // use Ctrl+C or Ctrl+Break to simulate + case CTRL_C_EVENT: // SERVICE_CONTROL_STOP in debug mode + _tprintf(TEXT("Stopping %s.\n"), TEXT(SZSERVICEDISPLAYNAME)); + ServiceStop(); + return TRUE; + break; + + } + + return FALSE; + +} // ControlHandler + + +///////////////////////////////////////////////////////////////////////// +LPTSTR +GetLastErrorText( + LPTSTR lpszBuf, + DWORD dwSize + ) +/*++ + +Routine Description: + + Copies last error message text to string. + +Arguments: + + lpszBuf - destination buffer + dwSize - size of buffer + +Return Values: + + destination buffer + +--*/ +{ + DWORD dwRet; + LPTSTR lpszTemp = NULL; + + dwRet = FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |FORMAT_MESSAGE_ARGUMENT_ARRAY, + NULL, + GetLastError(), + LANG_NEUTRAL, + (LPTSTR)&lpszTemp, + 0, + NULL ); + + // supplied buffer is not long enough + if ( !dwRet || ( (long)dwSize < (long)dwRet+14 ) ) + lpszBuf[0] = TEXT('\0'); + else { + lpszTemp[lstrlen(lpszTemp)-2] = TEXT('\0'); //remove cr and newline character + _stprintf( lpszBuf, TEXT("%s (0x%x)"), lpszTemp, GetLastError() ); + } + + if ( lpszTemp ) + LocalFree((HLOCAL) lpszTemp ); + + return lpszBuf; +} // GetLastErrorText diff --git a/private/net/svcdlls/lls/server/service.h b/private/net/svcdlls/lls/server/service.h new file mode 100644 index 000000000..c7a92147a --- /dev/null +++ b/private/net/svcdlls/lls/server/service.h @@ -0,0 +1,59 @@ +/*++ + +Copyright (c) 1994 Microsoft Corporation + +Module Name: + + Service.h + +Abstract: + + +Author: + + Arthur Hanson (arth) Dec 07, 1994 + +Environment: + +Revision History: + +--*/ + +#ifndef _SERVICE_H +#define _SERVICE_H + + +#ifdef __cplusplus +extern "C" { +#endif + + +// name of the executable +#define SZAPPNAME "LLSrv" + +// internal name of the service +#define SZSERVICENAME "LicenseLoggingService" + +// displayed name of the service +#define SZSERVICEDISPLAYNAME "License Logging Service" + +// list of service dependencies - "dep1\0dep2\0\0" +#define SZDEPENDENCIES "" +////////////////////////////////////////////////////////////////////////////// + + + +VOID ServiceStart(DWORD dwArgc, LPTSTR *lpszArgv); +VOID ServiceStop(); + + + +BOOL ReportStatusToSCMgr(DWORD dwCurrentState, DWORD dwWin32ExitCode, DWORD dwWaitHint); +VOID MessageLogWrite(LPTSTR lpszMsg); + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/private/net/svcdlls/lls/server/sources b/private/net/svcdlls/lls/server/sources new file mode 100644 index 000000000..f7128e073 --- /dev/null +++ b/private/net/svcdlls/lls/server/sources @@ -0,0 +1,84 @@ +!IF 0 + +Copyright (c) 1989 Microsoft Corporation + +Module Name: + + sources. + +Abstract: + + This file specifies the target component being built and the list of + sources files needed to build that component. Also specifies optional + compiler switches and libraries that are unique for the component being + built. + + +Author: + + Steve Wood (stevewo) 12-Apr-1990 + +NOTE: Commented description of this file is in \nt\bak\bin\sources.tpl + +!ENDIF + +MAJORCOMP=lls +MINORCOMP=server + +TARGETNAME=llssrv +TARGETPATH=obj +TARGETTYPE=LIBRARY + +INCLUDES=$(BASEDIR)\public\sdk\inc;$(BASEDIR)\private\inc;$(SOURCES_PATH)..\inc + +SOURCES= \ + $(SOURCES_PATH)certdb.c \ + $(SOURCES_PATH)llsutil.c \ + $(SOURCES_PATH)service.c \ + $(SOURCES_PATH)msvctbl.c \ + $(SOURCES_PATH)registry.c \ + $(SOURCES_PATH)mapping.c \ + $(SOURCES_PATH)purchase.c \ + $(SOURCES_PATH)perseat.c \ + $(SOURCES_PATH)server.c \ + $(SOURCES_PATH)repl.c \ + $(SOURCES_PATH)rpc.c \ + $(SOURCES_PATH)pack.c \ + $(SOURCES_PATH)scaven.c \ + $(SOURCES_PATH)llsrpc_s.c \ + $(SOURCES_PATH)lsapi_s.c \ + $(SOURCES_PATH)llsdbg_s.c \ + $(SOURCES_PATH)svctbl.c \ + $(SOURCES_PATH)llssrv.c \ + $(SOURCES_PATH)llssrv.rc + +UMTYPE=windows +UMAPPL=llssrv +UMRES=$(@R).res +NTTARGETFILE1=obj\*\llssrv.res +UMLIBS= \ + $(SOURCES_PATH)..\common\obj\*\llscomm.lib \ + obj\*\llssrv.lib \ + $(BASEDIR)\public\sdk\lib\*\rpcutil.lib \ + $(BASEDIR)\public\sdk\lib\*\rpcrt4.lib \ + $(BASEDIR)\public\sdk\lib\*\samlib.lib \ + $(BASEDIR)\public\sdk\lib\*\samsrv.lib \ + $(BASEDIR)\public\sdk\lib\*\ntdll.lib \ + $(BASEDIR)\public\sdk\lib\*\shell32.lib \ + $(BASEDIR)\public\sdk\lib\*\netapi32.lib + + +TARGETLIBS= \ + $(SOURCES_PATH)..\common\obj\*\llscomm.lib \ + $(BASEDIR)\public\sdk\lib\*\rpcutil.lib \ + $(BASEDIR)\public\sdk\lib\*\rpcrt4.lib \ + $(BASEDIR)\public\sdk\lib\*\rpcndr.lib \ + $(BASEDIR)\public\sdk\lib\*\kernel32.lib \ + $(BASEDIR)\public\sdk\lib\*\advapi32.lib \ + $(BASEDIR)\public\sdk\lib\*\samsrv.lib \ + $(BASEDIR)\public\sdk\lib\*\netapi32.lib \ + $(BASEDIR)\public\sdk\lib\*\nlrepl.lib + +C_DEFINES=$(C_DEFINES) -DRPC_NO_WINDOWS_H -DUNICODE -D_UNICODE + +USE_CRTDLL=1 diff --git a/private/net/svcdlls/lls/server/svctbl.c b/private/net/svcdlls/lls/server/svctbl.c new file mode 100644 index 000000000..a4e8e4307 --- /dev/null +++ b/private/net/svcdlls/lls/server/svctbl.c @@ -0,0 +1,902 @@ +/*++ + +Copyright (c) 1994 Microsoft Corporation + +Module Name: + + svctbl.c + +Abstract: + + Service Table routines. Handles all access to service table + for keeping track of running services and session counts to those + services. + + +Author: + + Arthur Hanson (arth) 07-Dec-1994 + +Revision History: + + Jeff Parham (jeffparh) 05-Dec-1995 + o Integrated per seat and per server purchase models for secure + certificates. + o Added logging of per server license rejections. + +--*/ + +#include <stdlib.h> +#include <nt.h> +#include <ntrtl.h> +#include <nturtl.h> +#include <windows.h> +#include <lm.h> + +#include "llsapi.h" +#include "debug.h" +#include "llssrv.h" +#include "registry.h" +#include "ntlsapi.h" +#include "mapping.h" +#include "msvctbl.h" +#include "svctbl.h" +#include "perseat.h" +#include "llsevent.h" +#include "llsutil.h" +#include "purchase.h" + + +// +// Must have ending space for version number placeholder! +// +#define FILE_PRINT "FilePrint " +#define FILE_PRINT_BASE "FilePrint" +#define FILE_PRINT_VERSION_NDX ( 9 ) + +#define REMOTE_ACCESS "REMOTE_ACCESS " +#define REMOTE_ACCESS_BASE "REMOTE_ACCESS" + +extern ULONG NumFilePrintEntries; +extern LPTSTR *FilePrintTable; + + +ULONG ServiceListSize = 0; +PSERVICE_RECORD *ServiceList = NULL; +PSERVICE_RECORD *ServiceFreeList = NULL; + + +RTL_RESOURCE ServiceListLock; + +int __cdecl MServiceRecordCompare(const void *arg1, const void *arg2); + + +///////////////////////////////////////////////////////////////////////// +VOID +ServiceListInit() + +/*++ + +Routine Description: + + Creates the service table, used for tracking the services and session + count. This will pull the initial services from the registry. + + The table is linear so a binary search can be used on the table, so + some extra records are initialized so that each time we add a new + service we don't have to do a realloc. We also assume that adding + new services is a relatively rare occurance, since we need to sort + it each time. + + The service table is guarded by a read and write semaphore. Multiple + reads can occur, but a write blocks everything. + + The service table has two default entries for FilePrint and REMOTE_ACCESS. + +Arguments: + + None. + +Return Value: + + None. + +--*/ + +{ + BOOL PerSeatLicensing; + ULONG SessionLimit; + PSERVICE_RECORD Service; + + RtlInitializeResource(&ServiceListLock); + + // + // Just need to init FilePrint values... + // + Service = ServiceListAdd(TEXT(FILE_PRINT), FILE_PRINT_VERSION_NDX ); + RegistryInitValues(TEXT(FILE_PRINT_BASE), &PerSeatLicensing, &SessionLimit); + + // + // Need to init RAS separatly as it uses File/Print Licenses. + // + Service = ServiceListAdd(TEXT(REMOTE_ACCESS), lstrlen(TEXT(REMOTE_ACCESS)) - 1); + if (Service != NULL) { + Service->MaxSessionCount = SessionLimit; + Service->PerSeatLicensing = PerSeatLicensing; + } + +} // ServiceListInit + + +///////////////////////////////////////////////////////////////////////// +int __cdecl ServiceListCompare(const void *arg1, const void *arg2) { + PSERVICE_RECORD Svc1, Svc2; + + Svc1 = (PSERVICE_RECORD) *((PSERVICE_RECORD *) arg1); + Svc2 = (PSERVICE_RECORD) *((PSERVICE_RECORD *) arg2); + + return lstrcmpi( Svc1->Name, Svc2->Name); + +} // ServiceListCompare + + +PSERVICE_RECORD +ServiceListFind( + LPTSTR ServiceName + ) + +/*++ + +Routine Description: + + Internal routine to actually do binary search on ServiceList, this + does not do any locking as we expect the wrapper routine to do this. + The search is a simple binary search. + +Arguments: + + ServiceName - + +Return Value: + + Pointer to found service table entry or NULL if not found. + +--*/ + +{ + LONG begin = 0; + LONG end = (LONG) ServiceListSize - 1; + LONG cur; + int match; + PSERVICE_RECORD Service; + +#if DBG + if (TraceFlags & TRACE_FUNCTION_TRACE) + dprintf(TEXT("LLS TRACE: ServiceListFind\n")); +#endif + if ((ServiceName == NULL) || (ServiceListSize == 0)) + return NULL; + + while (end >= begin) { + // go halfway in-between + cur = (begin + end) / 2; + Service = ServiceList[cur]; + + // compare the two result into match + match = lstrcmpi(ServiceName, Service->Name); + + if (match < 0) + // move new begin + end = cur - 1; + else + begin = cur + 1; + + if (match == 0) + return Service; + } + + return NULL; + +} // ServiceListFind + + +///////////////////////////////////////////////////////////////////////// +DWORD +VersionToDWORD(LPTSTR Version) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ + +{ + LPSTR pVer; + DWORD Ver = 0; + char tmpStr[10]; + +#if DBG + if (TraceFlags & TRACE_FUNCTION_TRACE) + dprintf(TEXT("LLS TRACE: VersionToDWORD\n")); +#endif + + if ((Version == NULL) || (*Version == TEXT('\0'))) + return Ver; + + // + // Do major version number + // + tmpStr[0] = '\0'; + WideCharToMultiByte(CP_ACP, 0, Version, -1, tmpStr, 10, NULL, NULL); + Ver = (ULONG) atoi(tmpStr); + Ver *= 0x10000; + + // + // Now minor - look for period + // + pVer = tmpStr; + while ((*pVer != '\0') && (*pVer != '.')) + pVer++; + + if (*pVer == '.') { + pVer++; + Ver += atoi(pVer); + } + + return Ver; + +} // VersionToDWORD + + +///////////////////////////////////////////////////////////////////////// +PSERVICE_RECORD +ServiceListAdd( + LPTSTR ServiceName, + ULONG VersionIndex + ) + +/*++ + +Routine Description: + + Adds a service to the service table. This will also cause a poll of + the registry to get the initial values for session limits and the + type of licensing being used. + +Arguments: + + ServiceName - + +Return Value: + + Pointer to added service table entry, or NULL if failed. + +--*/ + +{ + ULONG i; + ULONG SessionLimit = 0; + BOOL PerSeatLicensing = FALSE; + PSERVICE_RECORD NewService; + LPTSTR NewServiceName, pDisplayName, pFamilyDisplayName; + PSERVICE_RECORD CurrentRecord = NULL; + PMASTER_SERVICE_RECORD mService; + +#if DBG + if (TraceFlags & TRACE_FUNCTION_TRACE) + dprintf(TEXT("LLS TRACE: ServiceListAdd\n")); +#endif + // + // We do a double check here to see if another thread just got done + // adding the service, between when we checked last and actually got + // the write lock. + // + CurrentRecord = ServiceListFind(ServiceName); + if (CurrentRecord != NULL) { + return CurrentRecord; + } + + // + // Allocate space for table (zero init it). + // + if (ServiceList == NULL) { + ServiceList = (PSERVICE_RECORD *) LocalAlloc(LPTR, sizeof(PSERVICE_RECORD) ); + ServiceFreeList = (PSERVICE_RECORD *) LocalAlloc(LPTR, sizeof(PSERVICE_RECORD) ); + } else { + ServiceList = (PSERVICE_RECORD *) LocalReAlloc(ServiceList, sizeof(PSERVICE_RECORD) * (ServiceListSize + 1), LHND); + ServiceFreeList = (PSERVICE_RECORD *) LocalReAlloc(ServiceFreeList, sizeof(PSERVICE_RECORD) * (ServiceListSize + 1), LHND); + } + + // + // Make sure we could allocate service table + // + if ((ServiceList == NULL) || (ServiceFreeList == NULL)) { + ASSERT(FALSE); + ServiceFreeList = NULL; + ServiceList = NULL; + ServiceListSize = 0; + return NULL; + } + + // + // Allocate space for saving off Service Name - we will take a space, then + // the version string onto the end of the Product Name. Therefore the + // product name will be something like "Microsoft SQL 4.2a". We maintain + // a pointer to the version, so that we can convert the space to a NULL + // and then get the product and version string separatly. Keeping them + // together simplifies the qsort and binary search routines. + // + NewService = (PSERVICE_RECORD) LocalAlloc(LPTR, sizeof(SERVICE_RECORD)); + if (NewService == NULL) { + ASSERT(FALSE); + return NULL; + } + + ServiceList[ServiceListSize] = NewService; + ServiceFreeList[ServiceListSize] = NewService; + + NewServiceName = (LPTSTR) LocalAlloc(LPTR, (lstrlen(ServiceName) + 1) * sizeof(TCHAR)); + if (NewServiceName == NULL) { + ASSERT(FALSE); + LocalFree(NewService); + return NULL; + } + + // now copy it over... + NewService->Name = NewServiceName; + lstrcpy(NewService->Name, ServiceName); + + // + // Allocate space for Root Name + // + NewService->Name[VersionIndex] = TEXT('\0'); + NewServiceName = (LPTSTR) LocalAlloc(LPTR, (lstrlen(NewService->Name) + 1) * sizeof(TCHAR)); + + if (NewServiceName == NULL) { + ASSERT(FALSE); + LocalFree(NewService->Name); + LocalFree(NewService); + return NULL; + } + + lstrcpy(NewServiceName, NewService->Name); + NewService->Name[VersionIndex] = TEXT(' '); + + // point service structure to it... + NewService->FamilyName = NewServiceName; + + // + // Allocate space for Display Name + // + RegistryDisplayNameGet(NewService->FamilyName, NewService->Name, &pDisplayName); + + if (pDisplayName == NULL) { + ASSERT(FALSE); + LocalFree(NewService->Name); + LocalFree(NewService->FamilyName); + LocalFree(NewService); + return NULL; + } + + // point service structure to it... + NewService->DisplayName = pDisplayName; + + RegistryFamilyDisplayNameGet(NewService->FamilyName, NewService->DisplayName, &pFamilyDisplayName); + + if (pFamilyDisplayName == NULL) { + ASSERT(FALSE); + LocalFree(NewService->Name); + LocalFree(NewService->FamilyName); + LocalFree(NewService->DisplayName); + LocalFree(NewService); + return NULL; + } + + // point service structure to it... + NewService->FamilyDisplayName = pFamilyDisplayName; + + // + // Update table size and init entry, including reading init values + // from registry. + // + NewService->Version = VersionToDWORD(&ServiceName[VersionIndex + 1]); + + // Init values from registry... + RegistryInitService(NewService->FamilyName, &PerSeatLicensing, &SessionLimit); + + if ( PerSeatLicensing ) + { + // per seat mode + NewService->MaxSessionCount = 0; + } + else if ( ServiceIsSecure( NewService->DisplayName ) ) + { + // per server mode with a secure product; requires certificate + NewService->MaxSessionCount = ProductLicensesGet( NewService->DisplayName, TRUE ); + } + else + { + // per server mode with an unsecure product; use limit from registry + NewService->MaxSessionCount = SessionLimit; + } + + NewService->PerSeatLicensing = PerSeatLicensing; + NewService->SessionCount = 0; + NewService->Index = ServiceListSize; + RtlInitializeCriticalSection(&NewService->ServiceLock); + + if (lstrcmpi(ServiceName, TEXT(REMOTE_ACCESS))) { + RtlAcquireResourceExclusive(&MasterServiceListLock, TRUE); + mService = MasterServiceListAdd( NewService->FamilyDisplayName, NewService->DisplayName, NewService->Version); + RtlReleaseResource(&MasterServiceListLock); + + if (mService == NULL) { + ASSERT(FALSE); + } else { + NewService->MasterService = mService; + + // + // In case this got added from the local service list table and we + // didn't have a version # yet. + // + if (mService->Version == 0) { + PMASTER_SERVICE_ROOT ServiceRoot = NULL; + + // + // Fixup next pointer chain + // + ServiceRoot = mService->Family; + i = 0; + while ((i < ServiceRoot->ServiceTableSize) && (MasterServiceTable[ServiceRoot->Services[i]]->Version < NewService->Version)) + i++; + + mService->next = 0; + mService->Version = NewService->Version; + if (i > 0) { + if (MasterServiceTable[ServiceRoot->Services[i - 1]]->next == mService->Index + 1) + mService->next = 0; + else + mService->next = MasterServiceTable[ServiceRoot->Services[i - 1]]->next; + + if (MasterServiceTable[ServiceRoot->Services[i - 1]] != mService) + MasterServiceTable[ServiceRoot->Services[i - 1]]->next = mService->Index + 1; + } + + // Resort it in order of the versions + qsort((void *) ServiceRoot->Services, (size_t) ServiceRoot->ServiceTableSize, sizeof(PMASTER_SERVICE_RECORD), MServiceRecordCompare); + } + } + + } + + ServiceListSize++; + + // Have added the entry - now need to sort it in order of the service names + qsort((void *) ServiceList, (size_t) ServiceListSize, sizeof(PSERVICE_RECORD), ServiceListCompare); + + return NewService; +} // ServiceListAdd + + +///////////////////////////////////////////////////////////////////////// +VOID +ServiceListResynch( ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ + +{ + PSERVICE_RECORD Service; + BOOL PerSeatLicensing; + ULONG SessionLimit; + ULONG i = 0; + PSERVICE_RECORD FilePrintService; + +#if DBG + if (TraceFlags & TRACE_FUNCTION_TRACE) + dprintf(TEXT("LLS TRACE: ServiceListReSynch\n")); +#endif + if (ServiceList == NULL) + return; + + // + // Need to update list so get exclusive access. + // + RtlAcquireResourceExclusive(&ServiceListLock, TRUE); + + for (i = 0; i < ServiceListSize; i++) { + // + // Note: We will init REMOTE_ACCESS with bogus values here, but we + // reset it to the correct values below. Since we have exclusive access + // to the table, this is fine (and faster than always checking for + // REMOTE_ACCESS). + // + RegistryInitService((ServiceList[i])->FamilyName, &PerSeatLicensing, &SessionLimit); + + if ( PerSeatLicensing ) + { + // per seat mode + (ServiceList[i])->MaxSessionCount = 0; + } + else if ( ServiceIsSecure( (ServiceList[i])->DisplayName ) ) + { + // per server mode with a secure product; requires certificate + (ServiceList[i])->MaxSessionCount = ProductLicensesGet( (ServiceList[i])->DisplayName, TRUE ); + } + else + { + // per server mode with an unsecure product; use limit from registry + (ServiceList[i])->MaxSessionCount = SessionLimit; + } + + (ServiceList[i])->PerSeatLicensing = PerSeatLicensing; + } + + // + // Need to init RAS separatly as it uses File/Print Licenses. + // + Service = ServiceListFind(TEXT(REMOTE_ACCESS)); + FilePrintService = ServiceListFind(TEXT(FILE_PRINT)); + + ASSERT( NULL != Service ); + ASSERT( NULL != FilePrintService ); + + if ( ( NULL != Service ) && ( NULL != FilePrintService ) ) + { + Service->MaxSessionCount = FilePrintService->MaxSessionCount; + Service->PerSeatLicensing = FilePrintService->PerSeatLicensing; + } + + RtlReleaseResource(&ServiceListLock); + + return; +} // ServiceListResynch + + +///////////////////////////////////////////////////////////////////////// +NTSTATUS +DispatchRequestLicense( + ULONG DataType, + PVOID Data, + LPTSTR ServiceID, + ULONG VersionIndex, + BOOL IsAdmin, + ULONG *Handle + ) + +/*++ + +Routine Description: + + +Arguments: + + ServiceID - + + IsAdmin - + + Handle - + +Return Value: + + +--*/ + +{ + NTSTATUS Status = STATUS_SUCCESS; + PSERVICE_RECORD Service; + ULONG SessionCount; + ULONG TableEntry; + LPTSTR pServiceID; + BOOL NoLicense = FALSE; + BOOL PerSeat; + +#if DBG + if (TraceFlags & TRACE_FUNCTION_TRACE) + dprintf(TEXT("LLS TRACE: DispatchRequestLicense\n")); +#endif + + *Handle = 0xFFFFFFFF; + pServiceID = ServiceID; + + // we only need read access since we aren't adding at this point + RtlAcquireResourceShared( &ServiceListLock, TRUE ); + + // check if in FilePrint table, if so then we use FilePrint as the name + ServiceID[ VersionIndex ] = TEXT('\0'); + if ( ServiceFindInTable( ServiceID, FilePrintTable, NumFilePrintEntries, &TableEntry ) ) + { + pServiceID = TEXT(FILE_PRINT); + } + ServiceID[ VersionIndex ] = TEXT(' '); + + Service = ServiceListFind( pServiceID ); + + if (Service == NULL) + { + // couldn't find service in list, so add it + RtlConvertSharedToExclusive(&ServiceListLock); + Service = ServiceListAdd( pServiceID, VersionIndex ); + RtlConvertExclusiveToShared(&ServiceListLock); + } + + if (Service != NULL) + { + // service found or added successfully + + *Handle = (ULONG) Service->Index; + + RtlEnterCriticalSection(&Service->ServiceLock); + SessionCount = Service->SessionCount + 1; + +#if DBG + if (TraceFlags & TRACE_LICENSE_REQUEST) + dprintf(TEXT("LLS: [0x%lX] %s License: %ld of %ld\n"), Service, Service->Name, SessionCount, Service->MaxSessionCount); +#endif + + if (SessionCount > Service->HighMark) + { + Service->HighMark = SessionCount; + } + + PerSeat = Service->PerSeatLicensing; + + if ( (SessionCount > Service->MaxSessionCount) && !IsAdmin ) + { + if ( PerSeat ) + { + // for node licenses, allow limit to be exceeded + // NOTE: The above comparison doesn't even make sense for per seat + // licenses, since MaxSessionCount tracks only per server + // licenses, never per seat licenses! -JBP + Service->SessionCount++; + } + else + { + // zero tolerance for exceeding limits on concurrent licenses + + TCHAR szFullUserName[ MAX_DOMAINNAME_LENGTH + MAX_USERNAME_LENGTH + 3 ] = TEXT(""); + LPWSTR apszSubString[ 2 ]; + DWORD dwError = ERROR_SUCCESS; + + if ( NT_LS_USER_NAME == DataType ) + { + apszSubString[ 0 ] = (LPWSTR) Data; + } + else + { + TCHAR szUserName[ MAX_USERNAME_LENGTH + 1 ]; + TCHAR szDomainName[ MAX_DOMAINNAME_LENGTH + 1 ]; + DWORD cbUserName; + DWORD cbDomainName; + SID_NAME_USE snu; + + cbUserName = sizeof( szUserName ); + cbDomainName = sizeof( szDomainName ); + + if ( LookupAccountSid( NULL, (PSID) Data, szUserName, &cbUserName, szDomainName, &cbDomainName, &snu ) ) + { + lstrcpy( szFullUserName, szDomainName ); + lstrcat( szFullUserName, TEXT( "\\" ) ); + lstrcat( szFullUserName, szUserName ); + } + else + { + dwError = GetLastError(); + } + + apszSubString[ 0 ] = szFullUserName; + } + + apszSubString[ 1 ] = ServiceID; + + LogEvent( LLS_EVENT_USER_NO_LICENSE, 2, apszSubString, dwError ); + + NoLicense = TRUE; + } + } + else + { + // within valid license limits or user is an admin + Service->SessionCount++; + } + + RtlLeaveCriticalSection(&Service->ServiceLock); + RtlReleaseResource(&ServiceListLock); + + if ( PerSeat ) + { + // per node ("per seat") license + + // translate REMOTE_ACCESS into FILE_PRINT before adding to + // per seat license records + if ( !lstrcmpi( ServiceID, TEXT( REMOTE_ACCESS ) ) ) + { + RtlAcquireResourceShared(&ServiceListLock, TRUE); + Service = ServiceListFind(TEXT(FILE_PRINT)); + RtlReleaseResource(&ServiceListLock); + + ASSERT(Service != NULL); + } + + UserListUpdate( DataType, Data, Service ); + } + else + { + // concurrent use ("per server") license + if (NoLicense) + { + Status = LS_INSUFFICIENT_UNITS; + *Handle = 0xFFFFFFFF; + } + } + } + else + { + // could neither find nor create service entry + + RtlReleaseResource(&ServiceListLock); +#if DBG + dprintf( TEXT( "DispatchRequestLicense(): Could neither find nor create service entry.\n" ) ); +#endif + } + + return Status; +} // DispatchRequestLicense + + + +///////////////////////////////////////////////////////////////////////// +VOID +DispatchFreeLicense( + ULONG Handle + ) + +/*++ + +Routine Description: + + +Arguments: + + Handle - + +Return Value: + + None. + +--*/ + +{ + PSERVICE_RECORD Service; + +#if DBG + if (TraceFlags & TRACE_FUNCTION_TRACE) + dprintf(TEXT("LLS TRACE: DispatchFreeLicense\n")); +#endif + // + // We only need read access since we aren't adding at this point. + // + RtlAcquireResourceShared(&ServiceListLock, TRUE); + +#if DBG + if (TraceFlags & TRACE_LICENSE_FREE) + dprintf(TEXT("Free Handle: 0x%lX\n"), Handle); +#endif + if (Handle < ServiceListSize) { + Service = ServiceFreeList[Handle]; + RtlEnterCriticalSection(&Service->ServiceLock); + if (Service->SessionCount > 0) + Service->SessionCount--; + RtlLeaveCriticalSection(&Service->ServiceLock); + } else { +#if DBG + dprintf(TEXT("Passed invalid Free Handle: 0x%lX\n"), Handle); +#endif + } + + RtlReleaseResource(&ServiceListLock); + +} // DispatchFreeLicense + + +#if DBG +///////////////////////////////////////////////////////////////////////// +VOID +ServiceListDebugDump( ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ + +{ + ULONG i = 0; + + // + // Need to scan list so get read access. + // + RtlAcquireResourceShared(&ServiceListLock, TRUE); + + dprintf(TEXT("Service Table, # Entries: %lu\n"), ServiceListSize); + if (ServiceList == NULL) + goto ServiceListDebugDumpExit; + + for (i = 0; i < ServiceListSize; i++) { + if ((ServiceList[i])->PerSeatLicensing) + dprintf(TEXT("%3lu) PerSeat: Y MS: %4lu CS: %4lu HM: %4lu [%3lu] Svc: %s\n"), + i + 1, ServiceList[i]->MaxSessionCount, ServiceList[i]->SessionCount, ServiceList[i]->HighMark, ServiceList[i]->Index, ServiceList[i]->Name); + else + dprintf(TEXT("%3lu) PerSeat: N MS: %4lu CS: %4lu HM: %4lu [%3lu] Svc: %s\n"), + i + 1, ServiceList[i]->MaxSessionCount, ServiceList[i]->SessionCount, ServiceList[i]->HighMark, ServiceList[i]->Index, ServiceList[i]->Name); + } + +ServiceListDebugDumpExit: + RtlReleaseResource(&ServiceListLock); + + return; +} // ServiceListDebugDump + + +///////////////////////////////////////////////////////////////////////// +VOID +ServiceListDebugInfoDump( PVOID Data ) + +/*++ + +Routine Description: + + +Arguments: + + +Return Value: + + +--*/ + +{ + PSERVICE_RECORD CurrentRecord = NULL; + + dprintf(TEXT("Service Table, # Entries: %lu\n"), ServiceListSize); + + if (lstrlen((LPWSTR) Data) > 0) { + CurrentRecord = ServiceListFind((LPWSTR) Data); + if (CurrentRecord != NULL) { + if (CurrentRecord->PerSeatLicensing) + dprintf(TEXT(" PerSeat: Y MS: %4lu CS: %4lu HM: %4lu [%3lu] Svc: %s\n"), + CurrentRecord->MaxSessionCount, CurrentRecord->SessionCount, CurrentRecord->HighMark, CurrentRecord->Index, CurrentRecord->Name); + else + dprintf(TEXT(" PerSeat: N MS: %4lu CS: %4lu HM: %4lu [%3lu] Svc: %s\n"), + CurrentRecord->MaxSessionCount, CurrentRecord->SessionCount, CurrentRecord->HighMark, CurrentRecord->Index, CurrentRecord->Name); + } + } + +} // ServiceListDebugInfoDump + +#endif diff --git a/private/net/svcdlls/lls/server/svctbl.h b/private/net/svcdlls/lls/server/svctbl.h new file mode 100644 index 000000000..e5bbb3759 --- /dev/null +++ b/private/net/svcdlls/lls/server/svctbl.h @@ -0,0 +1,74 @@ +/*++ + +Copyright (c) 1994 Microsoft Corporation + +Module Name: + + SvcTbl.h + +Abstract: + + +Author: + + Arthur Hanson (arth) Dec 07, 1994 + +Environment: + +Revision History: + +--*/ + + +#ifndef _LLS_SVCTBL_H +#define _LLS_SVCTBL_H + + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct _SERVICE_RECORD { + DWORD Index; + LPTSTR Name; + LPTSTR DisplayName; + DWORD Version; + LPTSTR FamilyName; + LPTSTR FamilyDisplayName; + + PMASTER_SERVICE_RECORD MasterService; + + BOOL PerSeatLicensing; + + RTL_CRITICAL_SECTION ServiceLock; + ULONG SessionCount; // # sessions current active + ULONG MaxSessionCount; // Max # simultaneous sessions + ULONG HighMark; // Max # simultaneous sessions ever attempted +} SERVICE_RECORD, *PSERVICE_RECORD; + + +extern ULONG ServiceListSize; +extern PSERVICE_RECORD *ServiceList; +extern PSERVICE_RECORD *ServiceFreeList; +extern RTL_RESOURCE ServiceListLock; + + +VOID ServiceListInit(); +PSERVICE_RECORD ServiceListAdd( LPTSTR ServiceName, ULONG VersionIndex ); +PSERVICE_RECORD ServiceListFind( LPTSTR ServiceName ); +VOID ServiceListResynch( ); +NTSTATUS DispatchRequestLicense( ULONG DataType, PVOID Data, LPTSTR ServiceID, ULONG VersionIndex, BOOL IsAdmin, ULONG *Handle ); +VOID DispatchFreeLicense( ULONG Handle ); +DWORD VersionToDWORD(LPTSTR Version); + +#if DBG +VOID ServiceListDebugDump( ); +VOID ServiceListDebugInfoDump( ); +#endif + + +#ifdef __cplusplus +} +#endif + +#endif |