Christopher Nefcy
September 1994
Click to open or copy the files in the SECURITY sample application for this technical article.
Microsoft® Windows NT™ security is discussed in detail in the Win32® API Programmer's Reference. The Security Model is very powerful and has many features. Unfortunately, there are very few examples which show the usage of security on an object. Most of the examples show how to add security to an object to allow all users to access that object. However, most programmers want to be able to limit access to an object. This technical article will try to explain how to use the some of the security features and will also show some examples of programming the security features.
A brief overview of the Security Model will provide familiarity with relevant concepts and terminology. A more in-depth examination should be done using the Microsoft® Win32® Security API Overviews.
Every Microsoft Windows NT™ object (files, named pipes, registry keys, user objects, kernel objects, private objects, and so on) has its security attributes described by a security descriptor (SD). The security-descriptor contains information about the owner of the object and has an access-control list (ACL), which is a list specifying user and groups and their access permissions on that object.
There are two types of ACLs: discretionary and system. The discretionary ACL (DACL) is controlled by the owner of an object and specifies the access particular users or groups can have to that object. The system ACL (SACL) is controlled by the system administrator, and allows system-level security to be associated with the object. The usage of DACL and SACL APIs is very similar. However, the SACL APIs can only be used by a process with System Administrator privileges. Because most security programming does not involve setting the system-level security, the discussion here will focus on the DACL APIs.
The DACL contains an entry for each user, global group, or local group that is given access permission (whether allowing or denying access) to the object. Each of these entries is in the form of an access-control entry (ACE). An ACE contains an ACE_HEADER structure, along with the access permission for that ACE type, and the security identifier (SID). The ACE_HEADER defines the type of ACE (ACCESS_ALLOWED_ACE_TYPE or ACCESS_DENIED_ACE_TYPE), the size of the ACE, and control flags for the ACE. The access permission will determine the type of permission (that is, Read, Write, and so on) that the user or group has. The user or group is specified using its SID.
When a DACL does not have any ACEs, then no access rights have been explicitly granted. Therefore, access to the object is implicitly denied. However, when an object does not have a DACL, then no protection is assigned to the object and any access request is granted. An SD for an object is initially set to have a DACL with no ACEs, meaning that there is no access for any user. To give access to all users or groups, the DACL for the SD must be explicitly set to NULL.
There are two types of SDs used in the system: absolute or self-relative. The distinction between these two types is very important when programming. An absolute SD contains pointers to the DACL and ACE information, while the self-relative format contains information in a contiguous block of memory, so the DACLs and ACEs are positioned at offsets. The security APIs use both absolute and self-relative formats at different times. When asking for SD information, it is always returned by the system in self-relative format. The program can then walk through the offsets to obtains the proper information. However, when adding to or changing the SD, it must be in absolute format. When programming a change to the SD, for instance, this means that the program must read in SD in one format, convert it and write it back in the other format.
A user of a process is assigned an access token, which contains identifiers that represent the user and any group to which the user belongs. This access token is checked against the SD of an object to determine the permission of the user has with respect to that object. When a Client connects to a Server, the Server process can impersonate the access token of the Client process. This gives the Server the ability to check the access permission of the Client user.
To manipulate the security on an object, the SD of that object must be retrieved from or set back to the object. There are a number of APIs to perform these functions, depending on the type of object. For example, retrieving object SD APIs include GetFileSecurity, RegGetKeySecurity, GetKernelObjectSecurity, GetUserObjectSecurity, and GetPrivateObjectSecurity. Setting object SD APIs include SetFileSecurity, RegSetKeySecurity, SetKernelObjectSecurity, SetUserObjectSecurity, and SetPrivateObjectSecurity. When getting or setting the SD of an object, the program can limit its access to only certain parts of the security information: OWNER_SECURITY_INFORMATION, GROUP_SECURITY_INFORMATION, DACL_SECURITY_INFORMATION, and SACL_SECURITY_INFORMATION. It should be noted that only a process with Administrative access privileges can get the SACL_SECURITY_INFORMATION. If the process does not have those privileges, the API will fail when trying to retrieve the object SD.
Another way to get or set the security on an object is to use the SECURITY_ATTRIBUTES structure of that object. This structure contains a pointer to the SD associated with that object, and is returned when the object is created (for example, CreateFile, CreateNamedPipe, and so on). This structure can be manipulated directly by viewing or changing the SD pointer element of the structure. There are reasons for using both methods of manipulating object security.
The programming steps outlined below address some of the more common security tasks: giving everyone access to an object; adding a user or group to a new object; adding another user or group to an object with an existing SD; and checking the access permission of a Client program to an object on a Server. The outline shows the basic steps needed to perform these tasks, although there may also be other considerations. Most of these considerations deal with the size of the elements of the SD, which can be of variable length, and the type (absolute or self-relative) of the SD. These are explained under the subheading "Programming Considerations," below. Examples of coding these functions are shown in the "Sample Code" subheading below.
To give everyone access to an object that was just created (for example, a named pipe), the following actions are necessary:
To give a user or a group specific access to an new object, the following actions are necessary:
To add a user or group to an object with an existing SD, the following actions are necessary:
To check the access permission of a Client program to an object on the Server (assuming the Client program has already connected to the Server), the following actions are necessary on the Server:
One of the considerations of security programming is determining the size of the security structures. The SDs can be of variable length, as can the DACLs, ACEs and the SIDs. Therefore, when obtaining the SD of an object, or creating a new SD, there must be enough memory in the buffer to handle the full SD. The same holds true of the other security structures. The minimum length of an SD is SECURITY_DESCRIPTOR_MIN_LENGTH, which means there are not any ACLs or SIDs associated with that SD. For every ACL that is to be added to the SD, the length of that ACL must be incorporated into the length of the SD. As the DACL requires more buffer space, so too will the SD.
The length of a DACL (or SACL) must be large enough to contain the ACL header and all of the ACEs that are to be stored in the DACL. Each ACE (whether an ACCESS_ALLOWED_ACE_TYPE or an ACCESS_DENIED_ACE_TYPE) has the following structure:
typedef struct_ACCESS_ALLOWED_ACE
{
ACE_HEADER Header;
ACCESS_MASK Mask;
DWORD SidStart;
} ACCESS_ALLOWED_ACE;
This means that each ACE added to the DACL requires room for the ACE plus room for its SID, minus the size of the SidStart member (a DWORD). For example, the size of a DACL buffer large enough to contain three ACCESS_ALLOWED_ACE structures is:
cbDacl = sizeof (ACL) + 3 * ( sizeof ( ACCESS_ALLOWED_ACE) - sizeof ( DWORD) ) +
GetLengthSid ( pSid1) + GetLengthSid ( pSid2)+ GetLengthSid ( pSid3);
There are a number of APIs that return the length of a security structure, given that a pointer to the security structure has already been retrieved. These include GetSecurityDescriptorLength for the SD, GetAclInformation for the DACL, and GetLengthSid for the SID. Sometimes, there is a need to know the length of the security structure before retrieving it (for example, when the memory for the structures is being dynamically allocated throughout the program). Some of the objects have Query APIs, such as RegQueryInfoKey, that will return information about the object, including the length of the SD. Otherwise, APIs that retrieve the SD of an object have to be used (these APIs are listed above). The SD length parameter should be set at 0, the API called, and the return checked for ERROR_INSUFFICIENT_BUFFER. In this last case, the SD length parameter should be set to the length of the SD, so memory can be allocated for the SD, and the API called again to retrieve the SD.
Another programming consideration is whether the SD is in self-relative or absolute format. When retrieving an SD from the system using an API, the SD is always returned in self-relative format. When setting an SD using an API, or adding DACLs to an SD, it must be done in absolute format. When initializing a new SD, it is in absolute format.
This conversion is most needed when adding or deleting a user or group to the existing SD of an object. There are two APIs which convert between self-relative and absolute format (or vice-versa): MakeAbsoluteSD and MakeSelfRelativeSD. However, if another user or group is to be added to the SD, the SD may not have enough buffer space, and this must be taken into account when creating the absolute SD. One method to do this is to:
This is shown in the "Sample Code" section below.
Below are examples of sample code that show some methods to manipulate the security on an object. Also included is a Windows NT service installation program. This program does things such as changing the SD of a registry key, checking if the user who scheduled the process has Administrative privileges, creating local groups on the domain, and creating and setting registry keys and values. It provides some insight into some of the capabilities of NT programming. This code is for example only and is not supported.
The following code would give everyone access to an object that was just created (for example, a named pipe):
/*------------------------------------------------------------------
| Name: SetPipeSecurity
| Desc: sets up security to use on pipe to allow access to everyone;
| upon return, can use saPipeSecurity to set security on pipe
| when using CreateNamedPipe
------------------------------------------------------------------*/
VOID SetPipeSecurity ( VOID)
{
HANDLE hPipe;
SECURITY_ATTRIBUTES saPipeSecurity;
PSECURITY_DESCRIPTOR pPipeSD = NULL;
// security inits
memset ( ( VOID *) &saPipeSecurity, 0, sizeof ( SECURITY_ATTRIBUTES) );
// alloc & init SD
if ( ! ( pPipeSD = ( PSECURITY_DESCRIPTOR)
( malloc ( SECURITY_DESCRIPTOR_MIN_LENGTH)) ) )
return;
if ( ! InitializeSecurityDescriptor ( pPipeSD,
SECURITY_DESCRIPTOR_REVISION) )
return;
// set NULL DACL on the SD
if ( ! SetSecurityDescriptorDacl ( pPipeSD, TRUE, ( PACL) NULL, FALSE) )
return;
// now set up the security attributes
saPipeSecurity.nLength = sizeof ( SECURITY_ATTRIBUTES);
saPipeSecurity.bInheritHandle = TRUE;
saPipeSecurity.lpSecurityDescriptor = pPipeSD;
// now create named pipe with security
hPipe = CreateNamedPipe (
PIPENAME, // name of pipe
PIPE_ACCESS_DUPLEX | // Open mode
FILE_FLAG_OVERLAPPED, // use overlapped structure
PIPE_TYPE_MESSAGE | // message mode
PIPE_READMODE_MESSAGE |
PIPE_WAIT, // blocking
dwMaxNumberOfClients, // Max. number of instances
PIPEPKTSIZE, // Size of output buffer
PIPEPKTSIZE, // Size of input buffer
0L, // Time-out value (use default)
&saPipeSecurity ); // security flag
}
/* eof - SetPipeSecurity */
The following code would give a user or a group specific access to a new object:
/*------------------------------------------------------------------
| Name: CreateSDForRegKey
| Desc: creates SD with access for up to MAX_LIST users or groups
| passed into function:
| SID from the group or user
| permission access requested
------------------------------------------------------------------*/
DWORD CreateSDForRegKey ( LPTSTR pszGroupName[], DWORD dwAccessMask)
{
#define MAX_LIST 10
PSID pGroupSID [MAX_LIST];
DWORD dwGroupSIDCount = 0;
DWORD cbSID;
SID_NAME_USE snuGroup;
DWORD dwDomainSize = 80;
TCHAR szDomainName[80];
PSECURITY_DESCRIPTOR pAbsSD = NULL;
PACL pDACL;
DWORD dwSDRevision;
DWORD dwDACLLength = sizeof ( ACL);
SECURITY_DESCRIPTOR_CONTROL sdcSDControl;
PACL pNewDACL = NULL;
BOOL fAceFound = 0;
BOOL fHasDACL = FALSE;
BOOL fDACLDefaulted = FALSE;
ACCESS_ALLOWED_ACE *pDACLAce;
DWORD dwError = 0;
DWORD i;
// handle for security registry key
HKEY hSecurityRegKey = ( HKEY) 0;
// inits
for ( i = 0; i < MAX_LIST; i++)
pGroupSID[i] = NULL;
// count number of groups or users to be added; list ends in NULL
for ( i = 0; pszGroupName[i] != NULL; i++);
if ( i > MAX_LIST)
return ( ERROR_TOO_MANY_NAMES);
else
dwGroupSIDCount = i;
// get SIDs for each group or user passed in
for ( i = 0; i < dwGroupSIDCount; i++)
{
cbSID = GetSidLengthRequired (2);
pGroupSID[i] = ( PSID) malloc ( cbSID);
// loop if not enough room for SID; otherwise set to NULL
while ( ! LookupAccountName ( NULL, pszGroupName[i], pGroupSID[i],
&cbSID, szDomain, &dwDomainSize, &snuGroup) )
{
dwError = GetLastError();
if ( dwError == ERROR_INSUFFICIENT_BUFFER)
pGroupSID[i] = ( PSID) realloc ( pGroupSID[i], cbSID);
else
{
pGroupSID[i] = NULL;
break;
}
}
// check if found group or user
if ( pGroupSID[i])
{
// add to DACL length
dwDACLLength += ( sizeof ( ACCESS_ALLOWED_ACE) -
sizeof ( DWORD) + GetLengthSid ( pGroupSID[i]);
}
}
// get memory needed for new DACL
if ( ! ( pNewDACL = ( PACL) malloc ( dwDACLLength) ) )
return ( GetLastError());
// get memory for new SD
if ( ! ( pAbsSD = ( PSECURITY_DESCRIPTOR)
malloc ( SECURITY_DESCRIPTOR_MIN_LENGTH + dwDACLLength) ) )
{
dwError = GetLastError();
goto ErrorExit;
}
// init new SD
if ( ! InitializeSecurityDescriptor ( pAbsSD,
SECURITY_DESCRIPTOR_REVISION) )
{
dwError = GetLastError();
goto ErrorExit;
}
// init new DACL
if ( ! InitializeAcl ( pNewDACL, dwDACLLength, ACL_REVISION) )
{
dwError = GetLastError();
goto ErrorExit;
}
// now add new ACEs to new DACL
for ( i = 0; i < dwGroupSIDCount; i++)
{
// if there is a valid SID, then attach to the ACE and add to the DACL
if ( pGroupSID[i])
{
if ( ! AddAccessAllowedAce ( pNewDACL, ACL_REVISION, dwAccessMask,
pGroupSID[i]) )
{
dwError = GetLastError();
goto ErrorExit;
}
}
}
// check if everything went ok
if ( ! IsValidAcl ( pNewDACL) )
{
dwError = GetLastError();
goto ErrorExit;
}
// now set DACL to the SD
if ( ! SetSecurityDescriptorDacl ( pAbsSD, TRUE, pNewDACL,
fDACLDefaulted) )
{
dwError = GetLastError();
goto ErrorExit;
}
// check if everything went ok
if ( ! IsValidSecurityDescriptor ( pAbsSD) )
{
dwError = GetLastError();
goto ErrorExit;
}
// now open reg key to set security
// note: pzsRegKeyName is a global
if ( ( dwError = RegOpenKeyEx ( HKEY_LOCAL_MACHINE, pszRegKeyName, 0,
KEY_ALL_ACCESS, &hSecurityRegKey) ) )
goto ErrorExit;
// now set the reg key security (this will overwrite any existing security)
dwError = RegSetKeySecurity (
hSecurityRegKey,
(SECURITY_INFORMATION)( DACL_SECURITY_INFORMATION),
pAbsSD);
// close reg key
RegCloseKey ( hSecurityRegKey);
ErrorExit:
// free memory
if ( pAbsSD)
free ( ( VOID *) pAbsSD);
if ( pNewDACL)
free ( ( VOID *) pNewDACL);
return ( dwError);
}
/* eof - CreateSDForRegKey */
The following code would add a user or group to an object with an existing SD:
/*------------------------------------------------------------------
| Name: AddToRegKeySD
| Desc: adds SID to SD on reg key
| passed into function:
| SD in self-relative mode
| SID from the group or user
| permission access requested
------------------------------------------------------------------*/
DWORD AddToRegKeySD ( PSECURITY_DESCRIPTOR pRelSD, PSID pGroupSID,
DWORD dwAccessMask)
{
PSECURITY_DESCRIPTOR pAbsSD = NULL;
PACL pDACL;
DWORD dwSDLength = 0;
DWORD dwSDRevision;
DWORD dwDACLLength = 0;
SECURITY_DESCRIPTOR_CONTROL sdcSDControl;
PACL pNewDACL = NULL;
DWORD dwAddDACLLength = 0;
BOOL fAceFound = 0;
BOOL fHasDACL = FALSE;
BOOL fDACLDefaulted = FALSE;
ACCESS_ALLOWED_ACE *pDACLAce;
DWORD dwError = 0;
DWORD i;
// handle for security registry key
HKEY hSecurityRegKey = ( HKEY) 0;
// get SD control bits
if ( ! GetSecurityDescriptorControl ( pRelSD,
( PSECURITY_DESCRIPTOR_CONTROL) &sdcSDControl,
( LPDWORD) &dwSDRevision) )
return ( GetLastError() );
// check if DACL is present
if ( SE_DACL_PRESENT & sdcSDControl)
{
// get dacl
if ( ! GetSecurityDescriptorDacl ( pRelSD, ( LPBOOL) &fHasDACL,
( PACL *) &pDACL,
( LPBOOL) &fDACLDefaulted) )
return ( GetLastError());
// get dacl length
dwDACLLength = pDACL->AclSize;
// now check if SID's ACE is there
for ( i = 0; i < pDACL->AceCount; i++)
{
if ( ! GetAce ( pDACL, i, ( LPVOID *) &pDACLAce) )
return ( GetLastError());
// check if group sid is already there
if ( EqualSid ( ( PSID) &(pDACLAce->SidStart), pGroupSID) )
break;
}
// exit if found (means already has been set)
if ( i < pDACL->AceCount)
{
dwError = ERROR_GROUP_EXISTS;
return ( dwError);
}
// get length of new DACL
dwAddDACLLength = sizeof ( ACCESS_ALLOWED_ACE) -
sizeof ( DWORD) + GetLengthSid ( pGroupSID);
}
else
// get length of new DACL
dwAddDACLLength = sizeof ( ACL) + sizeof ( ACCESS_ALLOWED_ACE) -
sizeof ( DWORD) + GetLengthSid ( pGroupSID);
// get memory needed for new DACL
if ( ! ( pNewDACL = ( PACL) malloc ( dwDACLLength + dwAddDACLLength) ) )
return ( GetLastError());
// get the sd length
dwSDLength = GetSecurityDescriptorLength ( pRelSD);
// get memory for new SD
if ( ! ( pAbsSD = ( PSECURITY_DESCRIPTOR)
malloc ( dwSDLength + dwAddDACLLength) ) )
{
dwError = GetLastError();
goto ErrorExit;
}
// change self-relative SD to absolute by making new SD
if ( ! InitializeSecurityDescriptor ( pAbsSD,
SECURITY_DESCRIPTOR_REVISION) )
{
dwError = GetLastError();
goto ErrorExit;
}
// init new DACL
if ( ! InitializeAcl ( pNewDACL, dwDACLLength + dwAddDACLLength,
ACL_REVISION) )
{
dwError = GetLastError();
goto ErrorExit;
}
// now add in all of the ACEs into the new DACL (if org DACL is there)
if ( SE_DACL_PRESENT & sdcSDControl)
{
for ( i = 0; i < pDACL->AceCount; i++)
{
// get ace from original dacl
if ( ! GetAce ( pDACL, i, ( LPVOID *) &pDACLAce) )
{
dwError = GetLastError();
goto ErrorExit;
}
// now add ace to new dacl
if ( ! AddAccessAllowedAce ( pNewDACL,
ACL_REVISION,
pDACLAce->Mask,
( PSID) &(pDACLAce->SidStart) ) )
{
dwError = GetLastError();
goto ErrorExit;
}
}
}
// now add new ACE to new DACL
if ( ! AddAccessAllowedAce ( pNewDACL, ACL_REVISION, dwAccessMask,
pGroupSID) )
{
dwError = GetLastError();
goto ErrorExit;
}
// check if everything went ok
if ( ! IsValidAcl ( pNewDACL) )
{
dwError = GetLastError();
goto ErrorExit;
}
// now set security descriptor DACL
if ( ! SetSecurityDescriptorDacl ( pAbsSD, TRUE, pNewDACL,
fDACLDefaulted) )
{
dwError = GetLastError();
goto ErrorExit;
}
// check if everything went ok
if ( ! IsValidSecurityDescriptor ( pAbsSD) )
{
dwError = GetLastError();
goto ErrorExit;
}
// now open reg key to set security
// note: pzsRegKeyName is a global
if ( ( dwError = RegOpenKeyEx ( HKEY_LOCAL_MACHINE, pszRegKeyName, 0,
KEY_ALL_ACCESS, &hSecurityRegKey) ) )
goto ErrorExit;
// now set the reg key security (this will overwrite any existing security)
dwError = RegSetKeySecurity (
hSecurityRegKey,
(SECURITY_INFORMATION)( DACL_SECURITY_INFORMATION),
pAbsSD);
// close reg key
RegCloseKey ( hSecurityRegKey);
ErrorExit:
// free memory
if ( pAbsSD)
free ( ( VOID *) pAbsSD);
if ( pNewDACL)
free ( ( VOID *) pNewDACL);
return ( dwError);
}
/* eof - AddToRegKeySD */
The following code would obtain the SD from an existing object (i.e.- a registry key):
/*----------------------------------------------------------------
| Name: GetRegKeySecurity
| Desc: gets security registry sec. descriptor
| pRegKeySD is global of type PSECURITY_DESCRIPTOR; you must free
| the memory alloc'ed for this when done with the reg key
-----------------------------------------------------------------*/
DWORD GetRegKeySecurity ( szRegKey)
{
#define PERR(szApi,lError) printf ( "\n%s: Error %d from %s on line %d", \
__FILE__, lError, szApi, __LINE__);
HKEY hRegKey; // handle for register key
LONG lError = 0L; // reg errors
// (GetLastError won't work with registry calls)
CHAR szClassName[MAX_PATH] = ""; // Buffer for class name.
DWORD dwcClassLen = MAX_PATH; // Length of class string.
DWORD dwcSubKeys; // Number of sub keys.
DWORD dwcMaxSubKey; // Longest sub key size.
DWORD dwcMaxClass; // Longest class string.
DWORD dwcValues; // Number of values for this key.
DWORD dwcMaxValueName; // Longest Value name.
DWORD dwcMaxValueData; // Longest Value data.
DWORD dwcSDLength; // Security descriptor length
FILETIME ftLastWriteTime; // Last write time.
// open the security key
if ( ( lError = RegOpenKey ( HKEY_LOCAL_MACHINE, szRegKey, &hRegKey) ) )
{
PERR ( "RegOpenKey", lError);
return ( lError);
}
// get length of security descriptor
if ( ( lError = RegQueryInfoKey ( hRegKey, szClassName, &dwcClassLen,
NULL, &dwcSubKeys, &dwcMaxSubKey, &dwcMaxClass,
&dwcValues, &dwcMaxValueName, &dwcMaxValueData,
&dwcSDLength, &ftLastWriteTime) ) )
{
PERR ( "RegQueryInfoKey", lError);
goto CleanUp;
}
/* could have used this to get length of SD:
lError = RegGetKeySecurity ( hRegKey,
(SECURITY_INFORMATION)( OWNER_SECURITY_INFORMATION
| GROUP_SECURITY_INFORMATION
| DACL_SECURITY_INFORMATION),
pRegKeySD, &dwcSDLength);
// check if not enough memory returned
if ( lError != ERROR_INSUFFICIENT_BUFFER)
{
PERR ( "RegGetKeySecurity", lError);
goto CleanUp;
}
*/
// get SD memory
pRegKeySD = ( PSECURITY_DESCRIPTOR) LocalAlloc ( LPTR, ( UINT)dwcSDLength);
// now get SD
if ( ( lError = RegGetKeySecurity ( hRegKey,
(SECURITY_INFORMATION)( OWNER_SECURITY_INFORMATION
| GROUP_SECURITY_INFORMATION
| DACL_SECURITY_INFORMATION),
pRegKeySD, &dwcSDLength) ) )
{
PERR ( "RegGetKeySecurity", lError);
goto CleanUp;
}
// check if SD is good
if ( ! IsValidSecurityDescriptor ( pRegKeySD))
{
lError = GetLastError();
PERR ( "IsValidSecurityDescriptor", lError);
}
CleanUp:
// must close reg key
RegCloseKey ( hRegKey);
return ( lError);
}
/* eof - GetRegKeySecurity */
The following code would check the access permission of a Client program to an object on the Server (assuming the Client program has already connected to the Server):
/*------------------------------------------------------------------
| Name: GetUserNameAccess
| Desc: gets the client user name and checks their access rights
------------------------------------------------------------------*/
DWORD GetUserNameAccess ( HPIPE hPipe)
{
#define READ_ACCESS KEY_QUERY_VALUE
#define WRITE_ACCESS KEY_SET_VALUE
#define READWRITE_ACCESS KEY_QUERY_VALUE|KEY_SET_VALUE
CHAR szClientName[100];
DWORD dwCurInstances;
HANDLE hThread = NULL;
HANDLE hThreadToken = NULL;
DWORD dwDesiredAccess;
DWORD dwGrantedAccess;
SHORT sErrorFlag = 1;
GENERIC_MAPPING gmMapping;
PRIVILEGE_SET psPrivilege;
DWORD dwPrivilegeLength;
BOOL fStatus;
BOOL fReturn;
// inits
memset ( szClientName, 0, sizeof ( szClientName) );
dwClientAccessType = 0;
// get client name from handle info
if ( ! ( GetNamedPipeHandleState ( hPipe, NULL, &dwCurInstances, NULL,
NULL, ( LPTSTR) szClientName, sizeof ( szClientName) ) ) )
goto CleanUp;
// change thread's security with impersonation
if ( ! ImpersonateNamedPipeClient ( hPipe) )
goto CleanUp;
// now get token for thread to use with access check
hThread = GetCurrentThread ();
if ( ! OpenThreadToken ( hThread, TOKEN_ALL_ACCESS, FALSE, &hThreadToken))
goto CleanUp;
// if made it here, then no api errors, just access denied return
sErrorFlag = 0;
// check for read/write access
dwDesiredAccess = READWRITE_ACCESS;
dwPrivilegeLength = sizeof ( psPrivilege);
fReturn = AccessCheck ( pRegKeySD, hThreadToken, dwDesiredAccess,
&gmMapping, &psPrivilege, &dwPrivilegeLength,
&dwGrantedAccess, &fStatus);
// if access allowed, set access
if ( fReturn && fStatus)
{
dwClientAccessType = READWRITE_ACCESS;
goto CleanUp;
}
// check for read only access
dwDesiredAccess = READ_ACCESS;
dwPrivilegeLength = sizeof ( psPrivilege);
fReturn = AccessCheck ( pRegKeySD, hThreadToken, dwDesiredAccess,
&gmMapping, &psPrivilege, &dwPrivilegeLength,
&dwGrantedAccess, &fStatus);
// if access allowed, set access
if ( fReturn && fStatus)
{
dwClientAccessType = READ_ACCESS;
goto CleanUp;
}
// check for write only access
dwDesiredAccess = WRITE_ACCESS;
dwPrivilegeLength = sizeof ( psPrivilege);
fReturn = AccessCheck ( pRegKeySD, hThreadToken, dwDesiredAccess,
&gmMapping, &psPrivilege, &dwPrivilegeLength,
&dwGrantedAccess, &fStatus);
// if access allowed, set access
if ( fReturn && fStatus)
{
dwClientAccessType = WRITE_ACCESS;
goto CleanUp;
}
CleanUp:
// put security back to normal
RevertToSelf ();
// close up handles
if ( hThreadToken)
CloseHandle ( hThreadToken);
if ( hThread)
CloseHandle ( hThread);
// check for errors
if ( sErrorFlag)
return ( GetLastError() );
// check if access flag was set
if ( dwClientAccessType)
return ( 0);
else
return ( ERROR_ACCESS_DENIED);
}
/* eof - GetUserNameAccess */
The following is sample code that installs a service. This code provides many examples detailing different aspects of Win32 programming. Besides installing the service, it also creates registry keys, sets registry parameter values, changes the SD on registry keys, creates local groups on the domain, and checks the user for Administrative privileges.
/* ---------------------------------------------------------------------
| file: siaminst.c
| author: Chris Nefcy
| date: 7/20/93
| desc: installs SIAM service for windows NT
| args:
| return:
| compiler: dos32x
| links: see siaminst.mak
| rev:
----------------------------------------------------------------------- */
#include <windows.h>
#include <lm.h>
#include <stdio.h>
// defaults (change when necessary)
// VERSION will change; client s/w needs to match this
#define VERSION "1.00.00"
#define TIMEOUT 180
#define DOMAINNAME "MS-INTERNET"
#define SERVICENAME "SiamServer"
#define SERVICEPROGRAM "siamsrvc.exe"
#define INSTALLCONFIG "siaminst.cfg"
#define MAXCLIENTS 100
// registry keys
#define PARAMETERSKEY "SYSTEM\\CurrentControlSet\\Services\\SiamServer\\Parameters"
#define SECURITYKEY "SYSTEM\\CurrentControlSet\\Services\\SiamServer\\Security"
#define TCPIPKEY "SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters"
// registry values
#define VERSIONVALUE "Version"
#define TIMEOUTVALUE "Timeout"
#define MAXCLIENTSVALUE "MaxNumberOfClients"
#define TCPMAXCONNVALUE "TcpMaxConnectAttempts"
// used to limit the connect time to 20-25 seconds
#define TCPIP_MAXCONN 3
// client access types
#define READ_ACCESS KEY_QUERY_VALUE
#define WRITE_ACCESS KEY_SET_VALUE
#define READWRITE_ACCESS KEY_QUERY_VALUE|KEY_SET_VALUE
// handles
SC_HANDLE schService;
SC_HANDLE schSCManager;
// groups
struct GROUPDESCRIPT
{
CHAR *pszGroupName;
LPWSTR lpwszLocalGroupComment;
DWORD dwAccessMask;
} GroupDescript[] =
{
{ "SIAMFULL", L"SIAM Users with Full Access to Internet",
READWRITE_ACCESS},
{ "SIAMREAD", L"SIAM Users with Read Only Access to Internet",
READ_ACCESS},
{ "SIAMWRITE", L"SIAM Users with Write Only Access to Internet",
WRITE_ACCESS},
{ NULL, NULL, 0}
};
// security descriptors for security registry key
PSECURITY_DESCRIPTOR pSecurityRegKeySD = NULL;
// handle for security registry key
HKEY hSecurityRegKey = ( HKEY) 0;
// memory allocs/frees
LONG lAllocCnt = 0L;
LONG lFreeCnt = 0L;
// routines
extern DWORD AddGroupsToDomain ( VOID);
extern DWORD AddToSecurityDACL ( PSECURITY_DESCRIPTOR, PSID, DWORD);
extern DWORD CheckDomainName ( CHAR *, DWORD);
extern BOOL CheckForTcpip ( VOID);
extern VOID CloseRegKeySecurity ( VOID);
extern VOID DoExitFunction ( DWORD);
extern VOID ErrorMessage ( DWORD, CHAR *, ...);
extern VOID FreeMemory ( VOID * );
extern VOID *_GetMemory ( UINT, CHAR *, UINT);
extern DWORD GetRegKeySecurity ( CHAR *);
extern BOOL RunningAsAdministrator ( VOID);
extern DWORD SetParameters ( VOID);
#define GetMemory(size) _GetMemory(size, __FILE__, __LINE__)
// error messages
#define MSGERRDACLACCESS "ERR: INS1000-Could not add ACE to DACL"
#define MSGERRDACLACE "ERR: INS1001-Could not get DACL ACE"
#define MSGERRDACLINFOGET "ERR: INS1002-Could not get DACL information"
#define MSGERRDACLINFOSET "ERR: INS1003-Could not set DACL information"
#define MSGERRDACLINIT "ERR: INS1004-Could not initialize new DACL"
#define MSGERRDACLSECUREGET "ERR: INS1005-Could not get Security Descriptor
for DACL"
#define MSGERRDACLSECURESET "ERR: INS1006-Could not set Security Descriptor
with DACL"
#define MSGERRDACLVALID "ERR: INS1007-DACL is not valid"
#define MSGERRGROUPADD "ERR: INS2000-Group not added to Domain: %s"
#define MSGERRGROUPCONVERT "ERR: INS2001-Could not change Group Name
to UNICODE: %s"
#define MSGERRGROUPDOMAIN "ERR: INS2002-Some of the Groups have not been
added to the Domain correctly"
#define MSGERRGROUPNAME "ERR: INS2003-Group Name not found: %s"
#define MSGERRGROUPSECURE "ERR: INS2004-Group not given permission
to use SiamServer: %s"
#define MSGERRREGCREATE "ERR: INS3000-Could not create Reg key: %s"
#define MSGERRREGINFO "ERR: INS3001-Could not get Reg key info: %s"
#define MSGERRREGINVALID "ERR: INS3002-Invalid Reg key security desc.: %s"
#define MSGERRREGOPEN "ERR: INS3003-Could not open Reg key: %s"
#define MSGERRREGSECUREGET "ERR: INS3004-Could not get Reg key security: %s"
#define MSGERRREGSECURESET "ERR: INS3005-Could not set Reg key security"
#define MSGERRREGVALUEGET "ERR: INS3006-Could not get Reg value: %s"
#define MSGERRREGVALUESET "ERR: INS3007-Could not set Reg value: %s"
#define MSGERRSDCONTROLGET "ERR: INS4000-Could not get Security Descriptor
Control"
#define MSGERRSDINIT "ERR: INS4001-Could not initialize Security Descriptor"
#define MSGERRSDVALID "ERR: INS4002-Security Descriptor is not valid"
#define MSGERRUSEADMIN "ERR: INS5000-Must have Administrative privileges
to run this program"
#define MSGERRUSEALLOC "ERR: INS5001-Malloc failed (%u; %s; %u)"
#define MSGERRUSEDOMAIN "ERR: INS5002-This can only be installed on
Domain: %s (not: %s)"
#define MSGERRSRVCCREATE "ERR: INS6000-Could not create Service: %s
(with program: %s)"
#define MSGERRSRVCSCMGR "ERR: INS6001-Could not open the Service Control
Manager"
#define MSGERRSRVCSTART "ERR: INS6002-Could not start Service: %s"
#define MSGERRSRVCTCPIP "ERR: INS6003-TCP/IP needs to be installed
on this system"
#define MSGERRDOMAINCHK "ERR: INS7000-Could not access Domain"
#define MSGERRDOMAINCTRL "ERR: INS7001-Could not access Domain Controller"
#define MSGERRFILEOPEN "ERR: INS8000-Could not open file: %s"
#define MSGERRFILEREAD "ERR: INS8001-Could not read file: %s"
/*------------------------------------------------------------------
| Name: main
| Desc: main program
------------------------------------------------------------------*/
VOID main ( INT argc, CHAR **argv)
{
// change here if want manual start to SERVICE_DEMAND_START
DWORD dwStartType = SERVICE_AUTO_START;
CHAR szExePath[MAX_PATH];
DWORD dwError = 0;
// see if user is an administrator
if ( ! RunningAsAdministrator () )
{
ErrorMessage ( ERROR_ACCESS_DENIED, MSGERRUSEADMIN);
ExitProcess ( ERROR_ACCESS_DENIED);
}
// see if tcpip is installed on system already
if ( ! CheckForTcpip ())
{
ErrorMessage ( ERROR_BAD_ENVIRONMENT, MSGERRSRVCTCPIP);
ExitProcess ( ERROR_BAD_ENVIRONMENT);
}
// get full service program name
GetCurrentDirectory ( sizeof ( szExePath), szExePath);
strcat ( szExePath, "\\");
strcat ( szExePath, SERVICEPROGRAM);
// print notifications
printf ( "\n");
printf ( "Installation of the service:\n");
printf ( "----------------------------\n");
printf ( "Service name: %s\n", SERVICENAME);
printf ( "Service executable path: %s\n", szExePath);
printf ( "Service startup type: %s\n",
dwStartType == SERVICE_AUTO_START ? "AutoStart" : "ManualStart");
// open service control manager
if ( !( schSCManager = OpenSCManager ( NULL, NULL, SC_MANAGER_ALL_ACCESS)))
{
dwError = GetLastError();
ErrorMessage ( dwError, MSGERRSRVCSCMGR);
ExitProcess ( dwError);
}
// create the service
if ( !( schService = CreateService ( schSCManager, SERVICENAME,
SERVICENAME, SERVICE_ALL_ACCESS,
SERVICE_WIN32_OWN_PROCESS, dwStartType,
SERVICE_ERROR_NORMAL, szExePath,
NULL, NULL, NULL, NULL, NULL) ) )
{
dwError = GetLastError();
ErrorMessage ( dwError, MSGERRSRVCCREATE, SERVICENAME, szExePath);
ExitProcess ( dwError);
}
SetParameters ();
printf ( "Starting service: %s\n\n", SERVICENAME);
if ( ! StartService ( schService, 0, NULL) )
{
dwError = GetLastError();
ErrorMessage ( dwError, MSGERRSRVCSTART, SERVICENAME);
DoExitFunction ( dwError);
}
printf ( "SERVICE STARTED SUCCESSFULLY\n\n");
// now add groups and set security
if ( ( dwError = AddGroupsToDomain() ) )
ErrorMessage ( dwError, MSGERRGROUPDOMAIN);
DoExitFunction ( dwError);
}
/* eof - main */
/*----------------------------------------------------------------
| Name: AddGroupsToDomain
| Desc: adds all of the groups to domain and sets reg key security
----------------------------------------------------------------*/
DWORD AddGroupsToDomain ( VOID)
{
LOCALGROUP_INFO_1 LocalGroupInfo1;
LPWSTR lpwszDomainController;
WCHAR wszGroupName[MAX_PATH];
DWORD dwGroupSIDLength = 0;
DWORD dwGroupSIDMaxLength = 0;
PSID pGroupSID = NULL;
SID_NAME_USE snuGroupSID;
CHAR szDomainName[80];
DWORD dwDomainNameSize = sizeof ( szDomainName);
DWORD dwError = 0;
DWORD i;
printf ( "\n");
printf ( "Adding Groups to the Domain:\n");
printf ( "----------------------------\n");
if ( ( dwError = CheckDomainName ( szDomainName, dwDomainNameSize) ) )
{
ErrorMessage ( dwError, MSGERRDOMAINCHK);
return ( dwError);
}
printf ( "Domain Name: %s\n", szDomainName);
// get domain controller server name
if ( ( dwError = NetGetDCName ( NULL, NULL,
( LPBYTE *) &lpwszDomainController) ) )
{
ErrorMessage ( dwError, MSGERRDOMAINCTRL);
return ( dwError);
}
printf ( "Domain Controller: %ws\n\n", lpwszDomainController);
// now add groups to domain
for ( i = 0; GroupDescript[i].pszGroupName; i++)
{
// change args to long string
if ( ! MultiByteToWideChar ( CP_ACP, MB_PRECOMPOSED,
GroupDescript[i].pszGroupName, -1,
wszGroupName, sizeof ( wszGroupName) ) )
{
dwError = GetLastError();
ErrorMessage ( dwError, MSGERRGROUPCONVERT,
GroupDescript[i].pszGroupName);
return ( dwError);
}
// set name & comment
LocalGroupInfo1.lgrpi1_name = wszGroupName;
LocalGroupInfo1.lgrpi1_comment = GroupDescript[i].lpwszLocalGroupComment;
dwError = NetLocalGroupAdd ( lpwszDomainController, 1,
( LPBYTE) &LocalGroupInfo1, NULL);
if ( dwError)
{
// check if group already there
if ( dwError == ERROR_ALIAS_EXISTS)
{
printf ( "Group already in %s Domain: %s\n", szDomainName,
GroupDescript[i].pszGroupName);
dwError = 0;
}
else
{
ErrorMessage ( dwError, MSGERRGROUPADD, szDomainName,
GroupDescript[i].pszGroupName);
return ( dwError);
}
}
else
{
printf ( "Group added to %s Domain: %s\n", szDomainName,
GroupDescript[i].pszGroupName);
}
}
NetApiBufferFree ( lpwszDomainController);
// now need to wait until domain recognizes the group names
printf ( "\n\nWaiting for the Groups to be recognized by the %s Domain\n",
szDomainName);
printf ( "This may take a few minutes ");
// for each group, wait until recognized by domain
for ( i = 0; GroupDescript[i].pszGroupName; i++)
{
dwGroupSIDLength = 0;
dwDomainNameSize = sizeof ( szDomainName);
// loop until domain recognizes name
for ( ; ; )
{
// should always have an error (sid not big enough)
if ( ! LookupAccountName ( NULL, GroupDescript[i].pszGroupName,
pGroupSID, &dwGroupSIDLength, szDomainName,
&dwDomainNameSize, &snuGroupSID) )
{
dwError = GetLastError();
printf ( ".");
if ( dwError == ERROR_MORE_DATA ||
dwError == ERROR_INSUFFICIENT_BUFFER)
break;
}
}
// check if biggest sid length
if ( dwGroupSIDLength > dwGroupSIDMaxLength)
dwGroupSIDMaxLength = dwGroupSIDLength;
}
printf ( "\nGroups are now recognized by the %s Domain\n", szDomainName);
// now add groups to the reg key security
printf ( "\n");
printf ( "Setting Groups' permissions on the Registry Security Key:\n");
printf ( "---------------------------------------------------------\n");
printf ( "Registry key: HKEY_LOCAL_MACHINE\\%s\n\n", SECURITYKEY);
// get sid memory
if ( ! ( pGroupSID = ( PSID) GetMemory ( dwGroupSIDMaxLength) ) )
{
dwError = GetLastError();
return ( dwError);
}
// for each group, wait until recognized by domain
for ( i = 0; GroupDescript[i].pszGroupName; i++)
{
// get security descriptor from reg key
if ( ( dwError = GetRegKeySecurity ( SECURITYKEY) ) )
return ( dwError);
dwGroupSIDLength = dwGroupSIDMaxLength;
dwDomainNameSize = sizeof ( szDomainName);
// get account info
if ( ! LookupAccountName ( NULL, GroupDescript[i].pszGroupName,
pGroupSID, &dwGroupSIDLength, szDomainName,
&dwDomainNameSize, &snuGroupSID) )
{
dwError = GetLastError();
ErrorMessage ( dwError, MSGERRGROUPNAME,
GroupDescript[i].pszGroupName);
goto ErrorExit;
}
// now add this group to dacl
if ( ( dwError = AddToSecurityDACL ( pSecurityRegKeySD, pGroupSID,
GroupDescript[i].dwAccessMask) ) )
{
if ( dwError == ERROR_GROUP_EXISTS)
printf ( "Group already has permission to use SiamServer: %s\n",
GroupDescript[i].pszGroupName);
else
{
ErrorMessage ( dwError, MSGERRGROUPSECURE,
GroupDescript[i].pszGroupName);
goto ErrorExit;
}
}
CloseRegKeySecurity();
printf ( "Group given permission to use SiamServer: %s\n",
GroupDescript[i].pszGroupName);
}
printf ( "\n");
ErrorExit:
FreeMemory ( ( VOID *) pGroupSID);
CloseRegKeySecurity();
return ( dwError);
}
/* eof - AddGroupsToDomain */
/*-----------------------------------------------------------------
| Name: AddToSecurityDACL
| Desc: adds SID to SD
-----------------------------------------------------------------*/
DWORD AddToSecurityDACL ( PSECURITY_DESCRIPTOR pRelSD, PSID pGroupSID,
DWORD dwAccessMask)
{
PSECURITY_DESCRIPTOR pAbsSD = NULL;
PACL pDACL;
DWORD dwSDLength = 0;
DWORD dwSDRevision;
DWORD dwDACLLength = 0;
SECURITY_DESCRIPTOR_CONTROL sdcSDControl;
PACL pNewDACL = NULL;
DWORD dwAddDACLLength = 0;
BOOL fAceFound = 0;
BOOL fHasDACL = FALSE;
BOOL fDACLDefaulted = FALSE;
ACCESS_ALLOWED_ACE *pDACLAce;
DWORD dwError = 0;
DWORD i;
// get sd control bits
if ( ! GetSecurityDescriptorControl ( pRelSD,
( PSECURITY_DESCRIPTOR_CONTROL) &sdcSDControl,
( LPDWORD) &dwSDRevision) )
{
dwError = GetLastError();
ErrorMessage ( dwError, MSGERRSDCONTROLGET);
return ( dwError);
}
// check if dacl is present
if ( SE_DACL_PRESENT & sdcSDControl)
{
// get dacl
if ( ! GetSecurityDescriptorDacl ( pRelSD, ( LPBOOL) &fHasDACL,
( PACL *) &pDACL,
( LPBOOL) &fDACLDefaulted) )
{
dwError = GetLastError();
ErrorMessage ( dwError, MSGERRDACLSECUREGET);
return ( dwError);
}
// get dacl length
dwDACLLength = pDACL->AclSize;
// now check if SID ace is there
for ( i = 0; i < pDACL->AceCount; i++)
{
if ( ! GetAce ( pDACL, i, ( LPVOID *) &pDACLAce) )
{
dwError = GetLastError();
ErrorMessage ( dwError, MSGERRDACLACE);
return ( dwError);
}
// check if group sid is already there
if ( EqualSid ( ( PSID) &(pDACLAce->SidStart), pGroupSID) )
break;
}
// exit if found (means already has been set)
if ( i < pDACL->AceCount)
{
dwError = ERROR_GROUP_EXISTS;
return ( dwError);
}
// get length of new DACL
dwAddDACLLength = sizeof ( ACCESS_ALLOWED_ACE) -
sizeof ( DWORD) + GetLengthSid ( pGroupSID);
}
else
// get length of new DACL
dwAddDACLLength = sizeof ( ACL) + sizeof ( ACCESS_ALLOWED_ACE) -
sizeof ( DWORD) + GetLengthSid ( pGroupSID);
// get memory needed for new DACL
if ( ! ( pNewDACL = ( PACL) GetMemory ( dwDACLLength + dwAddDACLLength) ) )
{
dwError = GetLastError();
goto ErrorExit;
}
// get the sd length
dwSDLength = GetSecurityDescriptorLength ( pRelSD);
// get memory for new sd
if ( ! ( pAbsSD = ( PSECURITY_DESCRIPTOR)
GetMemory ( dwSDLength + dwAddDACLLength) ) )
{
dwError = GetLastError();
goto ErrorExit;
}
// change self-relative SD to absolute
if ( ! InitializeSecurityDescriptor ( pAbsSD,
SECURITY_DESCRIPTOR_REVISION) )
{
dwError = GetLastError();
ErrorMessage ( dwError, MSGERRSDINIT);
goto ErrorExit;
}
// init new dacl
if ( ! InitializeAcl ( pNewDACL, dwDACLLength + dwAddDACLLength,
ACL_REVISION) )
{
dwError = GetLastError();
ErrorMessage ( dwError, MSGERRDACLINIT);
goto ErrorExit;
}
// now add in all of the aces into the new dacl (if org dacl is there)
if ( SE_DACL_PRESENT & sdcSDControl)
{
for ( i = 0; i < pDACL->AceCount; i++)
{
// get ace from original dacl
if ( ! GetAce ( pDACL, i, ( LPVOID *) &pDACLAce) )
{
dwError = GetLastError();
ErrorMessage ( dwError, MSGERRDACLACE);
goto ErrorExit;
}
// now add ace to new dacl
if ( ! AddAccessAllowedAce ( pNewDACL,
ACL_REVISION,
pDACLAce->Mask,
( PSID) &(pDACLAce->SidStart) ) )
{
dwError = GetLastError();
ErrorMessage ( dwError, MSGERRDACLACCESS);
goto ErrorExit;
}
}
}
// now add new ace to new dacl
if ( ! AddAccessAllowedAce ( pNewDACL, ACL_REVISION, dwAccessMask,
pGroupSID) )
{
dwError = GetLastError();
ErrorMessage ( dwError, MSGERRDACLACCESS);
goto ErrorExit;
}
// check if everything went ok
if ( ! IsValidAcl ( pNewDACL) )
{
dwError = GetLastError();
ErrorMessage ( dwError, MSGERRDACLVALID);
goto ErrorExit;
}
// now set security descriptor dacl
if ( ! SetSecurityDescriptorDacl ( pAbsSD, TRUE, pNewDACL,
fDACLDefaulted) )
{
dwError = GetLastError();
ErrorMessage ( dwError, MSGERRDACLSECURESET);
goto ErrorExit;
}
// check if everything went ok
if ( ! IsValidSecurityDescriptor ( pAbsSD) )
{
dwError = GetLastError();
ErrorMessage ( dwError, MSGERRSDVALID);
goto ErrorExit;
}
// now set the reg key security
if ( ( dwError = RegSetKeySecurity ( hSecurityRegKey,
(SECURITY_INFORMATION)( DACL_SECURITY_INFORMATION),
pAbsSD) ) )
ErrorMessage ( dwError, MSGERRREGSECURESET);
ErrorExit:
// free memory
FreeMemory ( ( VOID *) pAbsSD);
FreeMemory ( ( VOID *) pNewDACL);
return ( dwError);
}
/* eof - AddToSecurityDACL */
/*-----------------------------------------------------------------
| Name: CheckDomainName
| Desc: gets the domain name & checks it is the right one
-----------------------------------------------------------------*/
DWORD CheckDomainName ( CHAR *pszDomainName, DWORD dwDomainNameSize)
{
// just guessing at using this well known name;
// might be a better way to find the domain name
#define CHECKUSERNAME "Domain Users"
DWORD dwGroupSIDLength = 0;
PSID psidGroupSID = NULL;
SID_NAME_USE snuGroupSID;
HANDLE hConfig;
CHAR szReadBuffer[MAX_PATH];
DWORD dwBytesRead;
DWORD dwError = 0;
DWORD i;
// inits
memset ( pszDomainName, 0, dwDomainNameSize);
// get the domain name
if ( ! LookupAccountName ( NULL, CHECKUSERNAME, psidGroupSID,
&dwGroupSIDLength, pszDomainName,
&dwDomainNameSize, &snuGroupSID))
{
dwError = GetLastError();
if ( dwError == ERROR_MORE_DATA ||
dwError == ERROR_INSUFFICIENT_BUFFER)
{
dwError = 0;
// get sid memory
if ( ! ( psidGroupSID = ( PSID) GetMemory ( dwGroupSIDLength) ) )
{
dwError = GetLastError();
return ( dwError);
}
// now get name
if ( ! LookupAccountName ( NULL, CHECKUSERNAME, psidGroupSID,
&dwGroupSIDLength, pszDomainName,
&dwDomainNameSize, &snuGroupSID) )
dwError = GetLastError();
FreeMemory ( ( VOID *) psidGroupSID);
}
if ( dwError)
{
ErrorMessage ( dwError, MSGERRGROUPNAME, CHECKUSERNAME);
return ( dwError);
}
}
// now get the domain from the config file
if ( ( hConfig = CreateFile ( INSTALLCONFIG, GENERIC_READ, FILE_SHARE_READ,
NULL, OPEN_EXISTING, 0, 0) )
== INVALID_HANDLE_VALUE)
{
dwError = GetLastError();
ErrorMessage ( dwError, MSGERRFILEOPEN, INSTALLCONFIG);
return ( dwError);
}
if ( ! ReadFile ( hConfig, ( LPVOID) szReadBuffer, sizeof ( szReadBuffer),
&dwBytesRead, NULL) )
{
dwError = GetLastError();
ErrorMessage ( dwError, MSGERRFILEREAD, INSTALLCONFIG);
}
CloseHandle ( hConfig);
if ( dwError)
return ( dwError);
// take out the \n or \r or other non-ascii char
for ( i = 0; i < strlen ( szReadBuffer); i++)
{
if ( szReadBuffer[i] == '\r' || szReadBuffer[i] == '\n')
{
szReadBuffer[i] = '\0';
break;
}
}
// now check if they are equal
if ( stricmp ( szReadBuffer, pszDomainName) )
{
dwError = ERROR_INVALID_DOMAIN_ROLE;
ErrorMessage ( dwError, MSGERRUSEDOMAIN, szReadBuffer, pszDomainName);
}
return ( dwError);
}
/* eof - CheckDomainName */
/*------------------------------------------------------------------
| Name: CheckForTcpip
| Desc: checks if tcpip has been installed on system
------------------------------------------------------------------*/
BOOL CheckForTcpip ( VOID)
{
HKEY hTcpipRegKey;
// open the tcpip parameters key
if ( RegOpenKeyEx ( HKEY_LOCAL_MACHINE, TCPIPKEY, 0,
KEY_ALL_ACCESS, &hTcpipRegKey) )
return ( FALSE);
// close key
RegCloseKey ( hTcpipRegKey);
return ( TRUE);
}
/* eof - CheckForTcpip */
/*-----------------------------------------------------------------
| Name: CloseRegKeySecurity
| Desc: release memory and close reg handle
-----------------------------------------------------------------*/
VOID CloseRegKeySecurity ( VOID)
{
// free up memory
FreeMemory ( ( VOID *) pSecurityRegKeySD);
// close down reg key
if ( hSecurityRegKey)
RegCloseKey ( hSecurityRegKey);
hSecurityRegKey = ( HKEY) 0;
}
/* eof - CloseRegKeySecurity */
/*------------------------------------------------------------------
| Name: DoExitFunction
| Desc: exits program
------------------------------------------------------------------*/
VOID DoExitFunction ( DWORD dwError)
{
// close service control mgr
if ( schSCManager)
CloseServiceHandle ( schSCManager);
// close service handle
if ( schService)
CloseServiceHandle ( schService);
// check if error
if ( dwError)
printf ( "\nSERVICE NOT SUCCESSFULLY INSTALLED\n\n");
else
printf ( "\nSERVICE SUCCESSFULLY INSTALLED\n\n");
fflush ( stdout);
ExitProcess ( dwError);
}
/* eof - DoExitFunction */
/*------------------------------------------------------------------
| Name: ErrorMessage
| Desc: prints error messages and exits
------------------------------------------------------------------*/
VOID ErrorMessage ( DWORD dwError, CHAR *szFormat, ...)
{
CHAR szBuffer[MAX_PATH];
va_list vaPtr;
// get formatted string
va_start ( vaPtr, szFormat);
vsprintf ( szBuffer, szFormat, vaPtr);
va_end ( vaPtr);
// put error code into string
if ( dwError)
sprintf ( &szBuffer[ strlen ( szBuffer)], " (error = %d)", dwError);
strcat ( szBuffer, "\r\n");
printf ( szBuffer);
// DoExitFunction ( dwError);
}
/* eof - ErrorMessage */
/*------------------------------------------------------------------
| Name: FreeMemory
| Desc: frees memory
------------------------------------------------------------------*/
VOID FreeMemory( VOID *vPtr )
{
// free memory if it is set
if ( vPtr)
{
if ( ! LocalFree ( ( HLOCAL) vPtr) )
lFreeCnt++;
vPtr = NULL;
}
}
/* eof - FreeMemory */
/*-------------------------------------------------------------------
| Name: _GetMemory ()
| Desc: This safe version of malloc provides a quick check that a malloc
| succeeds; it terminates with an error if malloc does not succeed.
| Production code, especially for a Windows or Presentation Manager
| environment, should use a more robust version of SafeMallocFunc().
|
| This function is not meant to be called directly. Instead, programs
| should call the SafeMalloc macro defined in SAMPLES.H as:
| #define GetMemory(size) _GetMemory(size, __FILE__, __LINE__)
-------------------------------------------------------------------*/
VOID *_GetMemory ( UINT cbSize, CHAR *pszFilename, UINT usLine)
{
VOID *vPtr;
if ( ( vPtr = ( VOID *) LocalAlloc ( LPTR, cbSize)) == NULL)
ErrorMessage ( GetLastError(), MSGERRUSEALLOC,
cbSize, pszFilename, usLine);
else
lAllocCnt++;
return ( vPtr);
}
/* eof - GetMemory */
/*------------------------------------------------------------------
| Name: GetRegKeySecurity
| Desc: gets security registry sec. descriptor
------------------------------------------------------------------*/
DWORD GetRegKeySecurity ( CHAR *pszRegKeyName)
{
LONG dwError = 0; // reg errors (GetLastError won't work)
CHAR szClassName[MAX_PATH] = ""; // Buffer for class name.
DWORD dwcClassLen = MAX_PATH; // Length of class string.
DWORD dwcSubKeys; // Number of sub keys.
DWORD dwcMaxSubKey; // Longest sub key size.
DWORD dwcMaxClass; // Longest class string.
DWORD dwcValues; // Number of values for this key.
DWORD dwcMaxValueName; // Longest Value name.
DWORD dwcMaxValueData; // Longest Value data.
DWORD dwcSDLength; // Security descriptor length
FILETIME ftLastWriteTime; // Last write time.
// open the security key
if ( ( dwError = RegOpenKeyEx ( HKEY_LOCAL_MACHINE, pszRegKeyName, 0,
KEY_ALL_ACCESS, &hSecurityRegKey) ) )
{
ErrorMessage ( dwError, MSGERRREGOPEN, pszRegKeyName);
goto ErrorExit;
}
// get length of security descriptor
if ( ( dwError = RegQueryInfoKey ( hSecurityRegKey, szClassName,
&dwcClassLen, NULL, &dwcSubKeys, &dwcMaxSubKey,
&dwcMaxClass, &dwcValues, &dwcMaxValueName,
&dwcMaxValueData, &dwcSDLength, &ftLastWriteTime) ) )
{
ErrorMessage ( dwError, MSGERRREGINFO, pszRegKeyName);
goto ErrorExit;
}
// get SD memory
if ( ! ( pSecurityRegKeySD = ( PSECURITY_DESCRIPTOR)
GetMemory ( ( UINT) dwcSDLength) ) )
{
dwError = GetLastError();
return ( dwError);
}
// now get SD
if ( ( dwError = RegGetKeySecurity ( hSecurityRegKey,
(SECURITY_INFORMATION)( DACL_SECURITY_INFORMATION),
pSecurityRegKeySD, &dwcSDLength) ) )
{
ErrorMessage ( dwError, MSGERRREGSECUREGET, pszRegKeyName);
goto ErrorExit;
}
// check if SD is good
if ( ! IsValidSecurityDescriptor ( pSecurityRegKeySD))
{
dwError = GetLastError();
ErrorMessage ( dwError, MSGERRREGINVALID, pszRegKeyName);
}
ErrorExit:
return ( dwError);
}
/* eof - GetRegKeySecurity */
/*------------------------------------------------------------------
| Name: RunningAsAdministrator
| Desc: checks if user has administrator privileges
| Notes: This function returns TRUE if the user identifier associated with
| this process is a member of the the Administrators group.
------------------------------------------------------------------*/
BOOL RunningAsAdministrator ( VOID)
{
BOOL fAdmin;
HANDLE hThread;
TOKEN_GROUPS *ptg = NULL;
DWORD cbTokenGroups;
DWORD dwGroup;
PSID psidAdmin;
SID_IDENTIFIER_AUTHORITY SystemSidAuthority= SECURITY_NT_AUTHORITY;
// First we must open a handle to the access token for this thread.
if ( !OpenThreadToken ( GetCurrentThread(), TOKEN_QUERY, FALSE, &hThread))
{
if ( GetLastError() == ERROR_NO_TOKEN)
{
// If the thread does not have an access token, we'll examine the
// access token associated with the process.
if (! OpenProcessToken ( GetCurrentProcess(), TOKEN_QUERY,
&hThread))
return ( FALSE);
}
else
return ( FALSE);
}
// Then we must query the size of the group information associated with
// the token. Note that we expect a FALSE result from GetTokenInformation
// because we've given it a NULL buffer. On exit cbTokenGroups will tell
// the size of the group information.
if ( GetTokenInformation ( hThread, TokenGroups, NULL, 0, &cbTokenGroups))
return ( FALSE);
// Here we verify that GetTokenInformation failed for lack of a large
// enough buffer.
if ( GetLastError() != ERROR_INSUFFICIENT_BUFFER)
return ( FALSE);
// Now we allocate a buffer for the group information.
// Since _alloca allocates on the stack, we don't have
// to explicitly deallocate it. That happens automatically
// when we exit this function.
if ( ! ( ptg= _alloca ( cbTokenGroups)))
return ( FALSE);
// Now we ask for the group information again.
// This may fail if an administrator has added this account
// to an additional group between our first call to
// GetTokenInformation and this one.
if ( !GetTokenInformation ( hThread, TokenGroups, ptg, cbTokenGroups,
&cbTokenGroups) )
return ( FALSE);
// Now we must create a System Identifier for the Admin group.
if ( ! AllocateAndInitializeSid ( &SystemSidAuthority, 2,
SECURITY_BUILTIN_DOMAIN_RID,
DOMAIN_ALIAS_RID_ADMINS,
0, 0, 0, 0, 0, 0, &psidAdmin) )
return ( FALSE);
// Finally we'll iterate through the list of groups for this access
// token looking for a match against the SID we created above.
fAdmin= FALSE;
for ( dwGroup= 0; dwGroup < ptg->GroupCount; dwGroup++)
{
if ( EqualSid ( ptg->Groups[dwGroup].Sid, psidAdmin))
{
fAdmin = TRUE;
break;
}
}
// Before we exit we must explicity deallocate the SID we created.
FreeSid ( psidAdmin);
return ( fAdmin);
}
/* eof - RunningAsAdministrator */
/*-----------------------------------------------------------------
| Name: SetParameters
| Desc: sets registry parameters for service
-----------------------------------------------------------------*/
DWORD SetParameters ( VOID)
{
HKEY hRegKey;
CHAR *pszVersion = VERSION;
DWORD dwTimeout = TIMEOUT;
DWORD dwMaxNumOfClients = MAXCLIENTS;
DWORD dwType;
DWORD dwcbData;
DWORD dwTcpMaxConn;
DWORD dwDisposition;
DWORD dwError = 0;
// create or open the parameters key
if ( ( dwError = RegCreateKeyEx ( HKEY_LOCAL_MACHINE, PARAMETERSKEY, 0,
"", REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS,
NULL, &hRegKey, &dwDisposition) ) )
{
ErrorMessage ( dwError, MSGERRREGCREATE, PARAMETERSKEY);
goto ErrorExit1;
}
// add version value
if ( ( dwError = RegSetValueEx ( hRegKey, VERSIONVALUE, 0, REG_SZ,
( LPBYTE) pszVersion, strlen ( pszVersion) + 1) ) )
{
ErrorMessage ( dwError, MSGERRREGVALUESET, VERSIONVALUE);
goto ErrorExit;
}
// add timeout value
if ( ( dwError = RegSetValueEx ( hRegKey, TIMEOUTVALUE, 0, REG_DWORD,
( LPBYTE) &dwTimeout, sizeof ( DWORD) ) ) )
{
ErrorMessage ( dwError, MSGERRREGVALUESET, TIMEOUTVALUE);
goto ErrorExit;
}
// add max # of clients value
if ( ( dwError = RegSetValueEx ( hRegKey, MAXCLIENTSVALUE, 0, REG_DWORD,
( LPBYTE) &dwMaxNumOfClients, sizeof ( DWORD) ) ) )
{
ErrorMessage ( dwError, MSGERRREGVALUESET, MAXCLIENTSVALUE);
goto ErrorExit;
}
// close key
RegCloseKey ( hRegKey);
printf ( "Service version: %s\n", pszVersion);
printf ( "Service timeout: %d\n", dwTimeout);
printf ( "Service max. number of clients: %d\n\n", dwMaxNumOfClients);
// open the tcpip parameters key
if ( ( dwError = RegOpenKeyEx ( HKEY_LOCAL_MACHINE, TCPIPKEY, 0,
KEY_ALL_ACCESS, &hRegKey) ) )
{
ErrorMessage ( dwError, MSGERRREGOPEN, TCPIPKEY);
goto ErrorExit1;
}
dwcbData = ( DWORD) sizeof ( dwTcpMaxConn);
// check what the parameter is set to
if ( RegQueryValueEx ( hRegKey, TCPMAXCONNVALUE, NULL,
&dwType, ( LPBYTE) &dwTcpMaxConn, &dwcbData) )
dwTcpMaxConn = 0;
if ( dwTcpMaxConn != TCPIP_MAXCONN)
{
dwTcpMaxConn = TCPIP_MAXCONN;
// add version value
if ( ( dwError = RegSetValueEx ( hRegKey, TCPMAXCONNVALUE, 0,
REG_DWORD, ( LPBYTE) &dwTcpMaxConn,
sizeof ( DWORD) ) ) )
{
ErrorMessage ( dwError, MSGERRREGVALUESET, TCPMAXCONNVALUE);
goto ErrorExit;
}
printf ( "TcpMaxConnectAttempts changed to: %d\n\n", dwTcpMaxConn);
}
ErrorExit:
// close key
RegCloseKey ( hRegKey);
ErrorExit1:
return ( dwError);
}
/* eof - SetParameters */