summaryrefslogtreecommitdiffstats
path: root/private/net/svcdlls/lls/server
diff options
context:
space:
mode:
Diffstat (limited to 'private/net/svcdlls/lls/server')
-rw-r--r--private/net/svcdlls/lls/server/certdb.c1511
-rw-r--r--private/net/svcdlls/lls/server/certdb.h129
-rw-r--r--private/net/svcdlls/lls/server/llssrv.c1087
-rw-r--r--private/net/svcdlls/lls/server/llssrv.h109
-rw-r--r--private/net/svcdlls/lls/server/llssrv.rc12
-rw-r--r--private/net/svcdlls/lls/server/llsutil.c982
-rw-r--r--private/net/svcdlls/lls/server/llsutil.h63
-rw-r--r--private/net/svcdlls/lls/server/makefile6
-rw-r--r--private/net/svcdlls/lls/server/makefile.inc1
-rw-r--r--private/net/svcdlls/lls/server/mapping.c756
-rw-r--r--private/net/svcdlls/lls/server/mapping.h70
-rw-r--r--private/net/svcdlls/lls/server/msvctbl.c712
-rw-r--r--private/net/svcdlls/lls/server/msvctbl.h151
-rw-r--r--private/net/svcdlls/lls/server/pack.c4306
-rw-r--r--private/net/svcdlls/lls/server/pack.h195
-rw-r--r--private/net/svcdlls/lls/server/perseat.c3351
-rw-r--r--private/net/svcdlls/lls/server/perseat.h235
-rw-r--r--private/net/svcdlls/lls/server/purchase.c865
-rw-r--r--private/net/svcdlls/lls/server/purchase.h114
-rw-r--r--private/net/svcdlls/lls/server/registry.c1707
-rw-r--r--private/net/svcdlls/lls/server/registry.h83
-rw-r--r--private/net/svcdlls/lls/server/repl.c641
-rw-r--r--private/net/svcdlls/lls/server/repl.h47
-rw-r--r--private/net/svcdlls/lls/server/rpc.c6121
-rw-r--r--private/net/svcdlls/lls/server/scaven.c198
-rw-r--r--private/net/svcdlls/lls/server/scaven.h38
-rw-r--r--private/net/svcdlls/lls/server/server.c754
-rw-r--r--private/net/svcdlls/lls/server/server.h84
-rw-r--r--private/net/svcdlls/lls/server/service.c625
-rw-r--r--private/net/svcdlls/lls/server/service.h59
-rw-r--r--private/net/svcdlls/lls/server/sources84
-rw-r--r--private/net/svcdlls/lls/server/svctbl.c902
-rw-r--r--private/net/svcdlls/lls/server/svctbl.h74
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, &LTime);
+ RtlTimeToTimeFields(&LTime, &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