windows-nt/Source/XPSP1/NT/base/ntsetup/msoobci/msoobci.c

801 lines
17 KiB
C
Raw Permalink Normal View History

2020-09-26 03:20:57 -05:00
/*++
Copyright (c) Microsoft Corporation. All Rights Reserved.
Module Name:
msoobci.c
Abstract:
Exception Pack installer helper DLL
Can be used as a co-installer, or called via setup app, or RunDll32 stub
This DLL is for internal distribution of exception packs to update
OS components.
Author:
Jamie Hunter (jamiehun) 2001-11-27
Revision History:
Jamie Hunter (jamiehun) 2001-11-27
Initial Version
--*/
#include "msoobcip.h"
//
// globals
//
HANDLE g_DllHandle;
OSVERSIONINFOEX g_VerInfo;
VOID
DebugPrint(
IN PCTSTR format,
IN ... OPTIONAL
)
/*++
Routine Description:
Send a formatted string to the debugger.
Arguments:
format - standard printf format string.
Return Value:
NONE.
--*/
{
TCHAR buf[1200]; // wvsprintf maxes at 1024.
va_list arglist;
va_start(arglist, format);
lstrcpy(buf,TEXT("MSOOBCI: "));
wvsprintf(buf+lstrlen(buf), format, arglist);
lstrcat(buf,TEXT("\n"));
OutputDebugString(buf);
}
//
// Called by CRT when _DllMainCRTStartup is the DLL entry point
//
BOOL
WINAPI
DllMain(
IN HANDLE DllHandle,
IN DWORD Reason,
IN LPVOID Reserved
)
{
switch(Reason) {
case DLL_PROCESS_ATTACH:
//
// global initialization
// - make a note of DllHandle
// - make a note of OS version
//
g_DllHandle = DllHandle;
ZeroMemory(&g_VerInfo,sizeof(g_VerInfo));
g_VerInfo.dwOSVersionInfoSize = sizeof(g_VerInfo);
if(!GetVersionEx((LPOSVERSIONINFO)&g_VerInfo)) {
g_VerInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
if(!GetVersionEx((LPOSVERSIONINFO)&g_VerInfo)) {
return FALSE;
}
}
break;
}
return TRUE;
}
HRESULT
GuidFromString(
IN LPCTSTR GuidString,
OUT GUID *GuidBinary
)
/*++
Routine Description:
Convert a GUID from String form to Binary form
Arguments:
GuidString - string form
GuidBinary - filled with binary form
Return Value:
S_OK or E_INVALIDARG
--*/
{
HRESULT res;
TCHAR String[64];
lstrcpyn(String,GuidString,ARRAY_SIZE(String));
res = IIDFromString(String,GuidBinary);
return res;
}
HRESULT
StringFromGuid(
IN GUID *GuidBinary,
OUT LPTSTR GuidString,
IN DWORD BufferSize
)
/*++
Routine Description:
Convert a GUID from Binary form to String form
Arguments:
GuidBinary - binary form
GuidString - filled with string form
BufferSize - length of GuidString buffer
Return Value:
S_OK or E_INVALIDARG
--*/
{
int res;
res = StringFromGUID2(GuidBinary,GuidString,(int)BufferSize);
if(res == 0) {
return E_INVALIDARG;
}
return S_OK;
}
HRESULT
VersionFromString(
IN LPCTSTR VerString,
OUT INT * VerMajor,
OUT INT * VerMinor,
OUT INT * VerBuild,
OUT INT * VerQFE
)
/*++
Routine Description:
Convert a high.low string to VerHigh and VerLow
Arguments:
VerString - string form
VerMajor/VerMinor/VerBuild/VerQFE - components of version
Return Value:
S_OK or E_INVALIDARG
--*/
{
HRESULT res;
LPTSTR VerPtr;
long val;
*VerMajor = *VerMinor = *VerBuild = *VerQFE = 0;
//
// skip leading white space
//
while((VerString[0] == TEXT(' ')) ||
(VerString[0] == TEXT('\t'))) {
VerString++;
}
if(VerString[0] == TEXT('\0')) {
//
// wildcard
//
return S_FALSE;
}
//
// get version major part (decimal)
//
if(!((VerString[0]>= TEXT('0')) &&
(VerString[0]<= TEXT('9')))) {
return E_INVALIDARG;
}
val = _tcstol(VerString,&VerPtr,10);
if((VerPtr == VerString) ||
((VerPtr-VerString)>5) ||
(val>65535) ||
(val<0)) {
return E_INVALIDARG;
}
*VerMajor = (WORD)val;
//
// followed by .decimal
// (version minor part)
//
if((VerPtr[0] != TEXT('.')) ||
!((VerPtr[1]>= TEXT('0')) &&
(VerPtr[1]<= TEXT('9')))) {
return E_INVALIDARG;
}
VerString = VerPtr+1;
val = _tcstol(VerString,&VerPtr,10);
if((VerPtr == VerString) ||
((VerPtr-VerString)>5) ||
(val>65535) ||
(val<0)) {
return E_INVALIDARG;
}
*VerMinor = (WORD)val;
//
// followed by .decimal
// (version build, optional)
//
if(VerPtr[0] == TEXT('.')) {
if(!((VerPtr[1]>= TEXT('0')) &&
(VerPtr[1]<= TEXT('9')))) {
return E_INVALIDARG;
}
VerString = VerPtr+1;
val = _tcstol(VerString,&VerPtr,10);
if((VerPtr == VerString) ||
((VerPtr-VerString)>5) ||
(val>65535) ||
(val<0)) {
return E_INVALIDARG;
}
*VerBuild = (WORD)val;
}
//
// followed by .decimal
// (version qfe, optional)
//
if(VerPtr[0] == TEXT('.')) {
if(!((VerPtr[1]>= TEXT('0')) &&
(VerPtr[1]<= TEXT('9')))) {
return E_INVALIDARG;
}
VerString = VerPtr+1;
val = _tcstol(VerString,&VerPtr,10);
if((VerPtr == VerString) ||
((VerPtr-VerString)>5) ||
(val>65535) ||
(val<0)) {
return E_INVALIDARG;
}
*VerQFE = (WORD)val;
}
//
// trailing white space
//
VerString = VerPtr;
while((VerString[0] == TEXT(' ')) ||
(VerString[0] == TEXT('\t'))) {
VerString++;
}
//
// not well formed?
//
if(VerString[0] != TEXT('\0')) {
return E_INVALIDARG;
}
return S_OK;
}
int
CompareCompVersion(
IN INT VerMajor,
IN INT VerMinor,
IN INT VerBuild,
IN INT VerQFE,
IN PSETUP_OS_COMPONENT_DATA SetupOsComponentData
)
/*++
Routine Description:
Compare a version against component information
Arguments:
VerMajor/VerMinor/VerBuild/VerQFE - version to check against
(can have wildcards)
SetupOsComponentData - component version
Return Value:
-1, version not as good as component
0, version equiv to component
1, version better than component
--*/
{
return CompareVersion(VerMajor,
VerMinor,
VerBuild,
VerQFE,
SetupOsComponentData->VersionMajor,
SetupOsComponentData->VersionMinor,
SetupOsComponentData->BuildNumber,
SetupOsComponentData->QFENumber
);
}
int
CompareVersion(
IN INT VerMajor,
IN INT VerMinor,
IN INT VerBuild,
IN INT VerQFE,
IN INT OtherMajor,
IN INT OtherMinor,
IN INT OtherBuild,
IN INT OtherQFE
)
/*++
Routine Description:
Compare a version against component information
Arguments:
VerMajor/VerMinor/VerBuild/VerQFE - version to check
(can have wildcards)
OtherMajor/OtherMinor/OtherBuid/OtherQFE - version to check against
Return Value:
-1, version not as good as component
0, version equiv to component
1, version better than component
--*/
{
if((VerMajor==-1)||(OtherMajor==-1)) {
return 0;
}
if(VerMajor<OtherMajor) {
return -1;
}
if(VerMajor>OtherMajor) {
return 1;
}
if((VerMinor==-1)||(OtherMinor==-1)) {
return 0;
}
if(VerMinor<OtherMinor) {
return -1;
}
if(VerMinor>OtherMinor) {
return 1;
}
if((VerBuild==-1)||(OtherBuild==-1)) {
return 0;
}
if(VerBuild<OtherBuild) {
return -1;
}
if(VerBuild>OtherBuild) {
return 1;
}
if((VerQFE==-1)||(OtherQFE==-1)) {
return 0;
}
if(VerQFE<OtherQFE) {
return -1;
}
if(VerQFE>OtherQFE) {
return 1;
}
return 0;
}
HRESULT
MakeSurePathExists(
IN LPTSTR Path
)
/*++
Routine Description:
Make sure named directory exists
Arguments:
Path - path to directory to create, must be a writable buffer
Return Value:
status as hresult
S_OK - path created
S_FALSE - path already exists
--*/
{
DWORD dwResult;
DWORD Status;
HRESULT hrStatus;
dwResult = GetFileAttributes(Path);
if((dwResult != (DWORD)(-1)) && ((dwResult & FILE_ATTRIBUTE_DIRECTORY)!=0)) {
//
// directory exists
//
return S_FALSE;
}
hrStatus = MakeSureParentPathExists(Path);
if(!SUCCEEDED(hrStatus)) {
return hrStatus;
}
if(!CreateDirectory(Path,NULL)) {
Status = GetLastError();
return HRESULT_FROM_WIN32(Status);
}
return S_OK;
}
HRESULT
MakeSureParentPathExists(
IN LPTSTR Path
)
/*++
Routine Description:
Make sure parent of named directory/file exists
Arguments:
Path - path to directory to create, must be a writable buffer
Return Value:
status as hresult
S_OK - path created
S_FALSE - path already exists
--*/
{
HRESULT hrStatus;
LPTSTR Split;
LPTSTR Base;
TCHAR Save;
//
// make sure we don't try to create the root
//
if((_istalpha(Path[0]) && (Path[1]==TEXT(':')))
&& ((Path[2] == TEXT('\\')) || (Path[2] == TEXT('/')))
&& (Path[3] != TEXT('\0'))) {
Base = Path+3;
} else {
//
// at this time, this code expects X:\... format
//
return E_FAIL;
}
Split = GetSplit(Base);
if(Split == Base) {
//
// strange, should have succeeded
//
return E_FAIL;
}
Save = *Split;
*Split = TEXT('\0');
hrStatus = MakeSurePathExists(Path);
*Split = Save;
return hrStatus;
}
LPTSTR GetBaseName(
IN LPCTSTR FileName
)
/*++
Routine Description:
Given a full path, return basename portion
Arguments:
FileName - full or partial path
Return Value:
status as hresult
--*/
{
LPTSTR BaseName = (LPTSTR)FileName;
for(; *FileName; FileName = CharNext(FileName)) {
switch (*FileName) {
case TEXT(':'):
case TEXT('/'):
case TEXT('\\'):
BaseName = (LPTSTR)CharNext(FileName);
break;
}
}
return BaseName;
}
LPTSTR GetSplit(
IN LPCTSTR FileName
)
/*++
Routine Description:
Split path at last '/' or '\\' (similar to GetBaseName)
Arguments:
FileName - full or partial path
Return Value:
status as hresult
--*/
{
LPTSTR SplitPos = (LPTSTR)FileName;
for(SplitPos; *FileName; FileName = CharNext(FileName)) {
switch (*FileName) {
case TEXT('/'):
case TEXT('\\'):
SplitPos = (LPTSTR)FileName;
break;
}
}
return SplitPos;
}
BOOL
WINAPI
IsInteractiveWindowStation(
)
/*++
Routine Description:
Determine if we are running on an interactive station vs non-interactive
// station (i.e., service)
Arguments:
none
Return Value:
True if interactive
--*/
{
HWINSTA winsta;
USEROBJECTFLAGS flags;
BOOL interactive = TRUE; // true unless we determine otherwise
DWORD lenNeeded;
winsta = GetProcessWindowStation();
if(!winsta) {
return interactive;
}
if(GetUserObjectInformation(winsta,UOI_FLAGS,&flags,sizeof(flags),&lenNeeded)) {
interactive = (flags.dwFlags & WSF_VISIBLE) ? TRUE : FALSE;
}
//
// don't call CLoseWindowStation
//
return interactive;
}
BOOL
WINAPI
IsUserAdmin(
VOID
)
/*++
Routine Description:
This routine returns TRUE if the caller's process has admin privs
Caller is NOT expected to be impersonating anyone and IS
expected to be able to open their own process and process
token.
Though we could use CheckTokenMembership
this function has to work on NT4
Arguments:
None.
Return Value:
TRUE - Caller has Administrator privs.
FALSE - Caller does not have Administrator privs.
--*/
{
BOOL fAdmin = FALSE;
HANDLE hToken = NULL;
DWORD dwStatus;
DWORD dwACLSize;
DWORD cbps = sizeof(PRIVILEGE_SET);
PACL pACL = NULL;
PSID psidAdmin = NULL;
PSECURITY_DESCRIPTOR psdAdmin = NULL;
PRIVILEGE_SET ps;
GENERIC_MAPPING gm;
SID_IDENTIFIER_AUTHORITY sia = SECURITY_NT_AUTHORITY;
BOOL Impersonated = FALSE;
//
// Prepare some memory
//
ZeroMemory(&ps, sizeof(ps));
ZeroMemory(&gm, sizeof(gm));
//
// Get the Administrators SID
//
if (AllocateAndInitializeSid(&sia, 2,
SECURITY_BUILTIN_DOMAIN_RID,
DOMAIN_ALIAS_RID_ADMINS,
0, 0, 0, 0, 0, 0, &psidAdmin) ) {
//
// Get the Asministrators Security Descriptor (SD)
//
psdAdmin = malloc(SECURITY_DESCRIPTOR_MIN_LENGTH);
if (psdAdmin) {
if(InitializeSecurityDescriptor(psdAdmin,SECURITY_DESCRIPTOR_REVISION)) {
//
// Compute size needed for the ACL then allocate the
// memory for it
//
dwACLSize = sizeof(ACCESS_ALLOWED_ACE) + 8 +
GetLengthSid(psidAdmin) - sizeof(DWORD);
pACL = (PACL)malloc(dwACLSize);
if(pACL) {
//
// Initialize the new ACL
//
if(InitializeAcl(pACL, dwACLSize, ACL_REVISION2)) {
//
// Add the access-allowed ACE to the DACL
//
if(AddAccessAllowedAce(pACL,ACL_REVISION2,
(ACCESS_READ | ACCESS_WRITE),psidAdmin)) {
//
// Set our DACL to the Administrator's SD
//
if (SetSecurityDescriptorDacl(psdAdmin, TRUE, pACL, FALSE)) {
//
// AccessCheck is downright picky about what is in the SD,
// so set the group and owner
//
SetSecurityDescriptorGroup(psdAdmin,psidAdmin,FALSE);
SetSecurityDescriptorOwner(psdAdmin,psidAdmin,FALSE);
//
// Initialize GenericMapping structure even though we
// won't be using generic rights
//
gm.GenericRead = ACCESS_READ;
gm.GenericWrite = ACCESS_WRITE;
gm.GenericExecute = 0;
gm.GenericAll = ACCESS_READ | ACCESS_WRITE;
//
// AccessCheck requires an impersonation token, so lets
// indulge it
//
Impersonated = ImpersonateSelf(SecurityImpersonation);
if (OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, FALSE, &hToken)) {
if (!AccessCheck(psdAdmin, hToken, ACCESS_READ, &gm,
&ps,&cbps,&dwStatus,&fAdmin)) {
fAdmin = FALSE;
}
CloseHandle(hToken);
}
}
}
}
free(pACL);
}
}
free(psdAdmin);
}
FreeSid(psidAdmin);
}
if(Impersonated) {
RevertToSelf();
}
return(fAdmin);
}
HRESULT
ConcatPath(
IN LPTSTR Path,
IN DWORD Len,
IN LPCTSTR NewPart
)
/*++
Routine Description:
Concat NewPart onto Path
Arguments:
Path - existing path
Len - length of buffer
NewPart - part to append
Return Value:
status as hresult
--*/
{
LPTSTR end = Path+lstrlen(Path);
TCHAR c;
BOOL add_slash = FALSE;
BOOL pre_slash = FALSE;
c = *CharPrev(Path,end);
if((c!= TEXT('\\')) && (c!= TEXT('/'))) {
add_slash = TRUE;
}
if(NewPart) {
c = NewPart[0];
if((c== TEXT('\\')) || (c== TEXT('/'))) {
if(add_slash) {
add_slash = FALSE;
} else {
NewPart = CharNext(NewPart);
}
}
}
if((DWORD)((end-Path)+lstrlen(NewPart)+(add_slash?1:0)) >= Len) {
return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
}
if(add_slash) {
end[0] = TEXT('\\');
end++;
}
if(NewPart) {
lstrcpy(end,NewPart);
} else {
end[0] = TEXT('\0');
}
return S_OK;
}