/*++ Copyright (c) 1996 Microsoft Corporation Module Name: sceutil.cpp Abstract: Shared APIs Author: Jin Huang Revision History: jinhuang 23-Jan-1998 merged from multiple modules --*/ #include "headers.h" #include "sceutil.h" #include "infp.h" #include #include "commonrc.h" #include "client\CGenericLogger.h" extern HINSTANCE MyModuleHandle; BOOL ScepInitNameTable() { LoadString(MyModuleHandle, IDS_EVERYONE, NameTable[0].Name, 36); LoadString(MyModuleHandle, IDS_CREATOR_OWNER, NameTable[1].Name, 36); LoadString(MyModuleHandle, IDS_CREATOR_GROUP, NameTable[2].Name, 36); LoadString(MyModuleHandle, IDS_CREATOR_OWNER_SERVER, NameTable[3].Name, 36); LoadString(MyModuleHandle, IDS_CREATOR_GROUP_SERVER, NameTable[4].Name, 36); LoadString(MyModuleHandle, IDS_DIALUP, NameTable[5].Name, 36); LoadString(MyModuleHandle, IDS_NETWORK, NameTable[6].Name, 36); LoadString(MyModuleHandle, IDS_BATCH, NameTable[7].Name, 36); LoadString(MyModuleHandle, IDS_INTERACTIVE, NameTable[8].Name, 36); LoadString(MyModuleHandle, IDS_SERVICE, NameTable[9].Name, 36); LoadString(MyModuleHandle, IDS_ANONYMOUS_LOGON, NameTable[10].Name, 36); LoadString(MyModuleHandle, IDS_PROXY, NameTable[11].Name, 36); LoadString(MyModuleHandle, IDS_ENTERPRISE_DOMAIN, NameTable[12].Name, 36); LoadString(MyModuleHandle, IDS_NAME_SELF, NameTable[13].Name, 36); LoadString(MyModuleHandle, IDS_AUTHENTICATED_USERS, NameTable[14].Name, 36); LoadString(MyModuleHandle, IDS_RESTRICTED, NameTable[15].Name, 36); LoadString(MyModuleHandle, IDS_TERMINAL_SERVER_USER, NameTable[16].Name, 36); LoadString(MyModuleHandle, IDS_LOCAL_SYSTEM, NameTable[17].Name, 36); LoadString(MyModuleHandle, IDS_LOCALSERVICE, NameTable[18].Name, 36); LoadString(MyModuleHandle, IDS_NETWORKSERVICE, NameTable[19].Name, 36); LoadString(MyModuleHandle, IDS_ADMINISTRATORS, NameTable[20].Name, 36); LoadString(MyModuleHandle, IDS_NAME_USERS, NameTable[21].Name, 36); LoadString(MyModuleHandle, IDS_NAME_GUESTS, NameTable[22].Name, 36); LoadString(MyModuleHandle, IDS_POWER_USERS, NameTable[23].Name, 36); LoadString(MyModuleHandle, IDS_ACCOUNT_OPERATORS, NameTable[24].Name, 36); LoadString(MyModuleHandle, IDS_SERVER_OPERATORS, NameTable[25].Name, 36); LoadString(MyModuleHandle, IDS_PRINT_OPERATORS, NameTable[26].Name, 36); LoadString(MyModuleHandle, IDS_BACKUP_OPERATORS, NameTable[27].Name, 36); LoadString(MyModuleHandle, IDS_REPLICATOR, NameTable[28].Name, 36); LoadString(MyModuleHandle, IDS_RAS_SERVERS, NameTable[29].Name, 36); LoadString(MyModuleHandle, IDS_PREW2KCOMPACCESS, NameTable[30 ].Name, 36); LoadString(MyModuleHandle, IDS_REMOTE_DESKTOP_USERS, NameTable[31].Name, 36); LoadString(MyModuleHandle, IDS_NETWORK_CONFIGURATION_OPERATORS, NameTable[32].Name, 36); return TRUE; } BOOL ScepLookupNameTable( IN PWSTR Name, OUT PWSTR *StrSid ) { if ( Name == NULL || Name[0] == L'\0' ) { return FALSE; } for (int i = 0; i < TABLE_SIZE; i++) { if ( _wcsicmp(NameTable[i].Name, Name) == 0 ) { //found match *StrSid = (PWSTR)ScepAlloc((UINT)0, (2+wcslen(NameTable[i].StrSid))*sizeof(WCHAR)); if ( *StrSid == NULL ) { return FALSE; } else { (*StrSid)[0] = L'*'; wcscpy((*StrSid)+1, NameTable[i].StrSid); return TRUE; } } } return FALSE; } INT ScepLookupPrivByName( IN PCWSTR Right ) /* ++ Routine Description: This routine looksup a user right in SCE_Rights table and returns the index component in SCE_Rights. The index component indicates the bit number for the user right. Arguments: Right - The user right to look up Return value: The index component in SCE_Rights table if a match is found, -1 for no match -- */ { DWORD i; for (i=0; i= 0 && Priv < cPrivCnt ) { Len = wcslen(SCE_Privileges[Priv].Name); if ( Name != NULL ) { if ( *NameLen >= Len ) wcscpy(Name, SCE_Privileges[Priv].Name); else { *NameLen = Len; return(SCESTATUS_BUFFER_TOO_SMALL); } } if ( NameLen != NULL) *NameLen = Len; return(SCESTATUS_SUCCESS); } else return SCESTATUS_RECORD_NOT_FOUND; } SCESTATUS SceInfpOpenProfile( IN PCWSTR ProfileName, IN HINF *hInf ) /* Routine Description: This routine opens a profile and returns a handle. This handle may be used when read information out of the profile using Setup APIs. The handle must be closed by calling SCECloseInfProfile. Arguments: ProfileName - The profile to open hInf - the address for inf handle Return value: SCESTATUS */ { if ( ProfileName == NULL || hInf == NULL ) { return(SCESTATUS_INVALID_PARAMETER); } // // Check to see if the INF file is opened OK. // SetupOpenInfFile is defined in setupapi.h // *hInf = SetupOpenInfFile(ProfileName, NULL, INF_STYLE_WIN4, NULL ); if (*hInf == INVALID_HANDLE_VALUE) return( ScepDosErrorToSceStatus( GetLastError() ) ); else return( SCESTATUS_SUCCESS); } SCESTATUS SceInfpCloseProfile( IN HINF hInf ) { if ( hInf != INVALID_HANDLE_VALUE ) SetupCloseInfFile( hInf ); return(SCESTATUS_SUCCESS); } SCESTATUS ScepConvertMultiSzToDelim( IN PWSTR pValue, IN DWORD Len, IN WCHAR DelimFrom, IN WCHAR Delim ) /* Convert the multi-sz delimiter \0 to space */ { DWORD i; for ( i=0; iNext ) { if ( pName2->Name == NULL ) { continue; } Count2++; } for ( pName1=pList1; pName1 != NULL; pName1 = pName1->Next ) { if ( pName1->Name == NULL ) { continue; } Count1++; for ( pName2=pList2; pName2 != NULL; pName2 = pName2->Next ) { if ( pName2->Name == NULL ) { continue; } if ( _wcsicmp(pName1->Name, pName2->Name) == 0 ) { // // find a match // break; // the second for loop } } if ( pName2 == NULL ) { // // does not find a match // return(FALSE); } } if ( Count1 != Count2 ) return(FALSE); return(TRUE); } DWORD WINAPI SceEnumerateServices( OUT PSCE_SERVICES *pServiceList, IN BOOL bServiceNameOnly ) /* Routine Description: Enumerate all services installed on the local system. The information returned include startup status and security descriptor on each service object. Arguments: pServiceList - the list of services returned. Must be freed by LocalFree return value: ERROR_SUCCESS Win32 error codes */ { SC_HANDLE hScManager=NULL; LPENUM_SERVICE_STATUS pEnumBuffer=NULL, pTempEnum; DWORD ResumeHandle=0, BytesNeeded, ServicesCount=0; DWORD BufSize=1024; // // check arguments // if ( NULL == pServiceList ) return(ERROR_INVALID_PARAMETER); // // open service control manager // hScManager = OpenSCManager( NULL, NULL, MAXIMUM_ALLOWED //SC_MANAGER_ALL_ACCESS // SC_MANAGER_CONNECT | // SC_MANAGER_ENUMERATE_SERVICE | // SC_MANAGER_QUERY_LOCK_STATUS ); if ( NULL == hScManager ) { return( GetLastError() ); } DWORD rc=NO_ERROR; DWORD i; DWORD status; if ( !bServiceNameOnly ) { // // Adjust privilege for setting SACL // status = SceAdjustPrivilege( SE_SECURITY_PRIVILEGE, TRUE, NULL ); // // if can't adjust privilege, ignore (will error out later if SACL is requested) // } do { // // enumerate all services // pEnumBuffer = (LPENUM_SERVICE_STATUS)LocalAlloc(LMEM_FIXED,(UINT)BufSize); if ( NULL == pEnumBuffer ) { rc = ERROR_NOT_ENOUGH_MEMORY; break; } if ( !EnumServicesStatus( hScManager, SERVICE_WIN32, // do not expose driver | SERVICE_DRIVER, SERVICE_STATE_ALL, pEnumBuffer, BufSize, &BytesNeeded, &ServicesCount, &ResumeHandle) ) { rc = GetLastError(); } else rc = ERROR_SUCCESS; pTempEnum = pEnumBuffer; // // Process each service // if ( rc == ERROR_SUCCESS || rc == ERROR_MORE_DATA || rc == ERROR_INSUFFICIENT_BUFFER ) { rc = ERROR_SUCCESS; for ( i=0; pEnumBuffer && ilpServiceName, pEnumBuffer->lpDisplayName, 0, NULL, 0, TRUE, pServiceList ); } else { // // query startup and security descriptor // status = ScepQueryAndAddService( hScManager, pEnumBuffer->lpServiceName, pEnumBuffer->lpDisplayName, pServiceList ); } if ( status != ERROR_SUCCESS ) { rc = status; break; } pEnumBuffer++; } } // // Free buffer for next enumeration // if ( pTempEnum ) { LocalFree(pTempEnum); pTempEnum = NULL; } pEnumBuffer = NULL; BufSize = BytesNeeded + 2; ServicesCount = 0; } while ( rc == ERROR_SUCCESS && BytesNeeded > 0 ); // // clear memory and close handle // CloseServiceHandle (hScManager); if ( rc != ERROR_SUCCESS ) { // // free memory in pServiceList // SceFreePSCE_SERVICES(*pServiceList); *pServiceList = NULL; } if ( !bServiceNameOnly ) { // // Adjust privilege for SACL // SceAdjustPrivilege( SE_SECURITY_PRIVILEGE, FALSE, NULL ); } return(rc); } DWORD ScepQueryAndAddService( IN SC_HANDLE hScManager, IN LPWSTR lpServiceName, IN LPWSTR lpDisplayName, OUT PSCE_SERVICES *pServiceList ) /* Routine Description: Queries the security descriptor of the service and add all information to PSCE_SERVICE list Arguments: hScManager - service control manager handle lpServiceName - The service name ServiceStatus - The service status pServiceList - The service list to output Return Value: ERROR_SUCCESS Win32 errors */ { SC_HANDLE hService; DWORD rc=ERROR_SUCCESS; if ( hScManager == NULL || lpServiceName == NULL || pServiceList == NULL ) { return(ERROR_INVALID_PARAMETER); } // // Open the service // SERVICE_ALL_ACCESS | // READ_CONTROL | // ACCESS_SYSTEM_SECURITY // hService = OpenService( hScManager, lpServiceName, MAXIMUM_ALLOWED | ACCESS_SYSTEM_SECURITY ); if ( hService != NULL ) { // // Query the startup type // DWORD BytesNeeded=0; DWORD BufSize; // // Query configuration (Startup type) // get size first // if ( !QueryServiceConfig( hService, NULL, 0, &BytesNeeded ) ) { rc = GetLastError(); if ( rc == ERROR_INSUFFICIENT_BUFFER ) { // // should always gets here // LPQUERY_SERVICE_CONFIG pConfig=NULL; pConfig = (LPQUERY_SERVICE_CONFIG)LocalAlloc(0, BytesNeeded+1); if ( pConfig != NULL ) { rc = ERROR_SUCCESS; BufSize=BytesNeeded; // // the real query for Startup type (pConfig->dwStartType) // if ( QueryServiceConfig( hService, pConfig, BufSize, &BytesNeeded ) ) { // // Query the security descriptor length // the following function does not take NULL for the // address of security descriptor so use a temp buffer first // to get the real length // BYTE BufTmp[128]; SECURITY_INFORMATION SeInfo; // // only query DACL and SACL information // /* SeInfo = DACL_SECURITY_INFORMATION | SACL_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | OWNER_SECURITY_INFORMATION; */ SeInfo = DACL_SECURITY_INFORMATION | SACL_SECURITY_INFORMATION; if ( !QueryServiceObjectSecurity( hService, SeInfo, (PSECURITY_DESCRIPTOR)BufTmp, 128, &BytesNeeded ) ) { rc = GetLastError(); if ( rc == ERROR_INSUFFICIENT_BUFFER || rc == ERROR_MORE_DATA ) { // // if buffer is not enough, it is ok // because BytesNeeded is the real length // rc = ERROR_SUCCESS; } } else rc = ERROR_SUCCESS; if ( rc == ERROR_SUCCESS ) { // // allocate buffer for security descriptor // PSECURITY_DESCRIPTOR pSecurityDescriptor=NULL; pSecurityDescriptor = (PSECURITY_DESCRIPTOR)LocalAlloc(LMEM_FIXED, BytesNeeded+2); if ( NULL != pSecurityDescriptor ) { // // query the security descriptor // BufSize = BytesNeeded; if ( QueryServiceObjectSecurity( hService, SeInfo, pSecurityDescriptor, BufSize, &BytesNeeded ) ) { // // create a service node and add it to the list // rc = ScepAddOneServiceToList( lpServiceName, lpDisplayName, pConfig->dwStartType, pSecurityDescriptor, SeInfo, TRUE, pServiceList ); } else { // // error query the security descriptor // rc = GetLastError(); } if ( rc != ERROR_SUCCESS ) { LocalFree(pSecurityDescriptor); } } else { // // cannot allocate memory for security descriptor // rc = ERROR_NOT_ENOUGH_MEMORY; } } } else { // // cannot query config // rc = GetLastError(); } LocalFree(pConfig); } else rc = ERROR_NOT_ENOUGH_MEMORY; } } else { // // should not fall in here, if it does, just return success // } CloseServiceHandle(hService); } else { // // cannot open service // rc = GetLastError(); } return(rc); } INT ScepLookupPrivByValue( IN DWORD Priv ) /* ++ Routine Description: This routine looksup a privilege in SCE_Privileges table and returns the index for the priv. Arguments: Priv - The privilege to look up Return value: The index in SCE_Privileges table if a match is found, or -1 for no match -- */ { DWORD i; if ( Priv == 0 ) return (-1); for ( i=0; i=0x0500 // // NT5+ // switch (theType) { case NtProductLanManNt: *srvProduct = SCESVR_DC_WITH_DS; break; case NtProductServer: *srvProduct = SCESVR_NT5_SERVER; break; case NtProductWinNt: *srvProduct = SCESVR_NT5_WKS; break; default: *srvProduct = SCESVR_UNKNOWN; } #else // // NT4 // switch (theType) { case NtProductLanManNt: *srvProduct = SCESVR_DC; break; case NtProductServer: *srvProduct = SCESVR_NT4_SERVER; break; case NtProductWinNt: *srvProduct = SCESVR_NT4_WKS; break; default: *srvProduct = SCESVR_UNKNOWN; } #endif } else { *srvProduct = SCESVR_UNKNOWN; } return(SCESTATUS_SUCCESS); } DWORD ScepAddTwoNamesToNameList( OUT PSCE_NAME_LIST *pNameList, IN BOOL bAddSeparator, IN PWSTR Name1, IN ULONG Length1, IN PWSTR Name2, IN ULONG Length2 ) /* ++ Routine Description: This routine adds two names (wchar) to the name list in the format of Name1\Name2, or Name1Name2, depends if bSeparator is TRUE. This routine is used for Domain\Account tracking list Arguments: pNameList - The name list to add to. Name1 - The name 1 to add Length1 - the length of name1 (number of wchars) Name2 - the name 2 to add Length2 - the length of name2 (number of wchars) Return value: Win32 error code -- */ { PSCE_NAME_LIST pList=NULL; ULONG Length; if ( pNameList == NULL ) return(ERROR_INVALID_PARAMETER); if ( Name1 == NULL && Name2 == NULL ) return(NO_ERROR); Length = Length1 + Length2; if ( Length <= 0 ) return(NO_ERROR); pList = (PSCE_NAME_LIST)ScepAlloc( (UINT)0, sizeof(SCE_NAME_LIST)); if ( pList == NULL ) return(ERROR_NOT_ENOUGH_MEMORY); if ( bAddSeparator ) { Length++; } pList->Name = (PWSTR)ScepAlloc( LMEM_ZEROINIT, (Length+1)*sizeof(TCHAR)); if ( pList->Name == NULL ) { ScepFree(pList); return(ERROR_NOT_ENOUGH_MEMORY); } if ( Name1 != NULL && Length1 > 0 ) wcsncpy(pList->Name, Name1, Length1); if ( bAddSeparator ) { wcsncat(pList->Name, L"\\", 1); } if ( Name2 != NULL && Length2 > 0 ) wcsncat(pList->Name, Name2, Length2); pList->Next = *pNameList; *pNameList = pList; return(NO_ERROR); } NTSTATUS ScepDomainIdToSid( IN PSID DomainId, IN ULONG RelativeId, OUT PSID *Sid ) /*++ Routine Description: Given a domain Id and a relative ID create a SID Arguments: DomainId - The template SID to use. RelativeId - The relative Id to append to the DomainId. Sid - Returns a pointer to an allocated buffer containing the resultant Sid. Free this buffer using NetpMemoryFree. Return Value: NTSTATUS --*/ { UCHAR DomainIdSubAuthorityCount; // Number of sub authorities in domain ID ULONG SidLength; // Length of newly allocated SID // // Allocate a Sid which has one more sub-authority than the domain ID. // DomainIdSubAuthorityCount = *(RtlSubAuthorityCountSid( DomainId )); SidLength = RtlLengthRequiredSid(DomainIdSubAuthorityCount+1); if ((*Sid = (PSID) ScepAlloc( (UINT)0, SidLength )) == NULL ) { return STATUS_NO_MEMORY; } // // Initialize the new SID to have the same inital value as the // domain ID. // if ( !NT_SUCCESS( RtlCopySid( SidLength, *Sid, DomainId ) ) ) { ScepFree( *Sid ); *Sid = NULL; return STATUS_INTERNAL_ERROR; } // // Adjust the sub-authority count and // add the relative Id unique to the newly allocated SID // (*(RtlSubAuthorityCountSid( *Sid ))) ++; *RtlSubAuthoritySid( *Sid, DomainIdSubAuthorityCount ) = RelativeId; return ERROR_SUCCESS; } DWORD ScepConvertSidToPrefixStringSid( IN PSID pSid, OUT PWSTR *StringSid ) /* The pair routine to convert stringsid to a Sid is ConvertStringSidToSid defined in sddl.h */ { if ( pSid == NULL || StringSid == NULL ) { return(ERROR_INVALID_PARAMETER); } UNICODE_STRING UnicodeStringSid; DWORD rc = RtlNtStatusToDosError( RtlConvertSidToUnicodeString(&UnicodeStringSid, pSid, TRUE )); if ( ERROR_SUCCESS == rc ) { *StringSid = (PWSTR)ScepAlloc(LPTR, UnicodeStringSid.Length+2*sizeof(WCHAR)); if ( *StringSid ) { (*StringSid)[0] = L'*'; wcsncpy( (*StringSid)+1, UnicodeStringSid.Buffer, UnicodeStringSid.Length/2); } else { rc = ERROR_NOT_ENOUGH_MEMORY; } RtlFreeUnicodeString( &UnicodeStringSid ); } return(rc); } NTSTATUS ScepConvertSidToName( IN LSA_HANDLE LsaPolicy, IN PSID AccountSid, IN BOOL bFromDomain, OUT PWSTR *AccountName, OUT DWORD *Length OPTIONAL ) { if ( LsaPolicy == NULL || AccountSid == NULL || AccountName == NULL ) { return(STATUS_INVALID_PARAMETER); } PSID pTmpSid=AccountSid; PLSA_TRANSLATED_NAME Names=NULL; PLSA_REFERENCED_DOMAIN_LIST ReferencedDomains=NULL; NTSTATUS NtStatus = LsaLookupSids( LsaPolicy, 1, (PSID *)&pTmpSid, &ReferencedDomains, &Names ); DWORD Len=0; if ( NT_SUCCESS(NtStatus) ) { if ( ( Names[0].Use != SidTypeInvalid && Names[0].Use != SidTypeUnknown ) ) { // // build the account name without domain name // if ( bFromDomain && Names[0].Use != SidTypeWellKnownGroup && ReferencedDomains->Entries > 0 && ReferencedDomains->Domains != NULL && Names[0].DomainIndex != -1 && (ULONG)(Names[0].DomainIndex) < ReferencedDomains->Entries && ReferencedDomains->Domains[Names[0].DomainIndex].Name.Length > 0 && ScepIsSidFromAccountDomain( ReferencedDomains->Domains[Names[0].DomainIndex].Sid ) ) { // // build domain name\account name // Len = Names[0].Name.Length + ReferencedDomains->Domains[Names[0].DomainIndex].Name.Length + 2; *AccountName = (PWSTR)LocalAlloc(LPTR, Len+sizeof(TCHAR)); if ( *AccountName ) { wcsncpy(*AccountName, ReferencedDomains->Domains[Names[0].DomainIndex].Name.Buffer, ReferencedDomains->Domains[Names[0].DomainIndex].Name.Length/2); (*AccountName)[ReferencedDomains->Domains[Names[0].DomainIndex].Name.Length/2] = L'\\'; wcsncpy((*AccountName)+ReferencedDomains->Domains[Names[0].DomainIndex].Name.Length/2+1, Names[0].Name.Buffer, Names[0].Name.Length/2); } else { NtStatus = STATUS_NO_MEMORY; } Len /= 2; } else { Len = Names[0].Name.Length/2; *AccountName = (PWSTR)LocalAlloc(LPTR, Names[0].Name.Length+2); if ( *AccountName ) { wcsncpy(*AccountName, Names[0].Name.Buffer, Len); } else { NtStatus = STATUS_NO_MEMORY; } } } else { NtStatus = STATUS_NONE_MAPPED; } } if ( ReferencedDomains ) { LsaFreeMemory(ReferencedDomains); ReferencedDomains = NULL; } if ( Names ) { LsaFreeMemory(Names); Names = NULL; } if ( NT_SUCCESS(NtStatus) && Length ) { *Length = Len; } return(NtStatus); } NTSTATUS ScepConvertNameToSid( IN LSA_HANDLE LsaPolicy, IN PWSTR AccountName, OUT PSID *AccountSid ) { if ( LsaPolicy == NULL || AccountName == NULL || AccountSid == NULL ) { return(STATUS_INVALID_PARAMETER); } PLSA_REFERENCED_DOMAIN_LIST RefDomains=NULL; PLSA_TRANSLATED_SID2 Sids=NULL; NTSTATUS NtStatus = ScepLsaLookupNames2( LsaPolicy, LSA_LOOKUP_ISOLATED_AS_LOCAL, AccountName, &RefDomains, &Sids ); if ( NT_SUCCESS(NtStatus) && Sids ) { // // build the account sid // if ( Sids[0].Use != SidTypeInvalid && Sids[0].Use != SidTypeUnknown && Sids[0].Sid != NULL ) { // // this name is mapped, the SID is in Sids[0].Sid // DWORD SidLength = RtlLengthSid(Sids[0].Sid); if ( (*AccountSid = (PSID) ScepAlloc( (UINT)0, SidLength)) == NULL ) { NtStatus = STATUS_NO_MEMORY; } else { // // copy the SID // NtStatus = RtlCopySid( SidLength, *AccountSid, Sids[0].Sid ); if ( !NT_SUCCESS(NtStatus) ) { ScepFree( *AccountSid ); *AccountSid = NULL; } } } else { NtStatus = STATUS_NONE_MAPPED; } } if ( Sids ) { LsaFreeMemory(Sids); } if ( RefDomains ) { LsaFreeMemory(RefDomains); } return(NtStatus); } NTSTATUS ScepLsaLookupNames2( IN LSA_HANDLE PolicyHandle, IN ULONG Flags, IN PWSTR pszAccountName, OUT PLSA_REFERENCED_DOMAIN_LIST *ReferencedDomains, OUT PLSA_TRANSLATED_SID2 *Sids ) /*++ Routine Description: Similar to LsaLookupNames2 except that on local lookup failures, it resolves free text accounts to the domain this machine is joined to Arguments: PolicyHandle - handle to LSA Flags - usually LSA_LOOKUP_ISOLATED_AS_LOCAL pszAccountName - name of account to lookup ReferencedDomains - returns the reference domain id (to be freed by caller) Sids - returns the SID looked up (to be freed by caller) Return Value: NTSTATUS --*/ { PWSTR pszScopedName = NULL; UNICODE_STRING UnicodeName; PPOLICY_ACCOUNT_DOMAIN_INFO pDomainInfo = NULL; RtlInitUnicodeString(&UnicodeName, pszAccountName); NTSTATUS NtStatus = LsaLookupNames2( PolicyHandle, Flags, 1, &UnicodeName, ReferencedDomains, Sids ); if ((NtStatus == STATUS_SOME_NOT_MAPPED || NtStatus == STATUS_NONE_MAPPED) && NULL == wcschr(pszAccountName, L'\\') ) { NtStatus = LsaQueryInformationPolicy(PolicyHandle, PolicyDnsDomainInformation, ( PVOID * )&pDomainInfo ); if (!NT_SUCCESS(NtStatus) || pDomainInfo == NULL || pDomainInfo->DomainName.Buffer == NULL || pDomainInfo->DomainName.Length <= 0) { NtStatus = STATUS_SOME_NOT_MAPPED; goto ExitHandler; } pszScopedName = (PWSTR) LocalAlloc(LMEM_ZEROINIT, (pDomainInfo->DomainName.Length/2 + wcslen(pszAccountName) + 2) * sizeof(WCHAR)); if (pszScopedName == NULL) { NtStatus = STATUS_NO_MEMORY; goto ExitHandler; } wcsncpy(pszScopedName, pDomainInfo->DomainName.Buffer, pDomainInfo->DomainName.Length/2); wcscat(pszScopedName, L"\\"); wcscat(pszScopedName, pszAccountName); RtlInitUnicodeString(&UnicodeName, pszScopedName); NtStatus = LsaLookupNames2( PolicyHandle, Flags, 1, &UnicodeName, ReferencedDomains, Sids ); } ExitHandler: if (pszScopedName) { LocalFree(pszScopedName); } if (pDomainInfo) { LsaFreeMemory( pDomainInfo ); } if ( STATUS_SUCCESS != NtStatus ) return STATUS_NONE_MAPPED; else return STATUS_SUCCESS; } SCESTATUS ScepConvertNameToSidString( IN LSA_HANDLE LsaHandle, IN PWSTR Name, IN BOOL bAccountDomainOnly, OUT PWSTR *SidString, OUT DWORD *SidStrLen ) { if ( LsaHandle == NULL || Name == NULL ) { return(SCESTATUS_INVALID_PARAMETER); } if ( Name[0] == L'\0' ) { return(SCESTATUS_INVALID_PARAMETER); } if ( SidString == NULL || SidStrLen == NULL ) { return(SCESTATUS_INVALID_PARAMETER); } // // convert the sid string to a real sid // PSID pSid=NULL; NTSTATUS NtStatus; DWORD rc; PLSA_REFERENCED_DOMAIN_LIST RefDomains=NULL; PLSA_TRANSLATED_SID2 Sids=NULL; NtStatus = ScepLsaLookupNames2( LsaHandle, LSA_LOOKUP_ISOLATED_AS_LOCAL, Name, &RefDomains, &Sids ); rc = RtlNtStatusToDosError(NtStatus); if ( ERROR_SUCCESS == rc && Sids ) { // // name is found, make domain\account format // if ( Sids[0].Use != SidTypeInvalid && Sids[0].Use != SidTypeUnknown && Sids[0].Sid != NULL ) { // // this name is mapped // if ( !bAccountDomainOnly || ScepIsSidFromAccountDomain( Sids[0].Sid ) ) { // // convert to a sid string, note: a prefix "*" should be added // UNICODE_STRING UnicodeStringSid; rc = RtlNtStatusToDosError( RtlConvertSidToUnicodeString(&UnicodeStringSid, Sids[0].Sid, TRUE )); if ( ERROR_SUCCESS == rc ) { *SidStrLen = UnicodeStringSid.Length/2 + 1; *SidString = (PWSTR)ScepAlloc(LPTR, UnicodeStringSid.Length + 2*sizeof(WCHAR)); if ( *SidString ) { (*SidString)[0] = L'*'; wcsncpy((*SidString)+1, UnicodeStringSid.Buffer, (*SidStrLen)-1); } else { *SidStrLen = 0; rc = ERROR_NOT_ENOUGH_MEMORY; } RtlFreeUnicodeString( &UnicodeStringSid ); } } else { // // add only the account name // rc = ERROR_NONE_MAPPED; } } else { rc = ERROR_NONE_MAPPED; } } if ( Sids ) { LsaFreeMemory(Sids); } if ( RefDomains ) { LsaFreeMemory(RefDomains); } return(ScepDosErrorToSceStatus(rc)); } SCESTATUS ScepLookupSidStringAndAddToNameList( IN LSA_HANDLE LsaHandle, IN OUT PSCE_NAME_LIST *pNameList, IN PWSTR LookupString, IN ULONG Len ) { if ( LsaHandle == NULL || LookupString == NULL ) { return(SCESTATUS_INVALID_PARAMETER); } if ( LookupString[0] == L'\0' ) { return(SCESTATUS_INVALID_PARAMETER); } if ( Len <= 3 || (LookupString[1] != L'S' && LookupString[1] != L's') || LookupString[2] != L'-' ) { return(SCESTATUS_INVALID_PARAMETER); } // // convert the sid string to a real sid // PSID pSid=NULL; NTSTATUS NtStatus; DWORD rc; PLSA_REFERENCED_DOMAIN_LIST RefDomains=NULL; PLSA_TRANSLATED_NAME Names=NULL; if ( ConvertStringSidToSid(LookupString+1, &pSid) ) { NtStatus = LsaLookupSids( LsaHandle, 1, &pSid, &RefDomains, &Names ); rc = RtlNtStatusToDosError(NtStatus); LocalFree(pSid); pSid = NULL; } else { rc = GetLastError(); } if ( ERROR_SUCCESS == rc && Names && RefDomains ) { // // name is found, make domain\account format // if ( ( Names[0].Use != SidTypeInvalid && Names[0].Use != SidTypeUnknown ) ) { // // this name is mapped // if ( RefDomains->Entries > 0 && Names[0].Use != SidTypeWellKnownGroup && RefDomains->Domains != NULL && Names[0].DomainIndex != -1 && (ULONG)(Names[0].DomainIndex) < RefDomains->Entries && RefDomains->Domains[Names[0].DomainIndex].Name.Length > 0 && ScepIsSidFromAccountDomain( RefDomains->Domains[Names[0].DomainIndex].Sid ) ) { // // add both domain name and account name // rc = ScepAddTwoNamesToNameList( pNameList, TRUE, RefDomains->Domains[Names[0].DomainIndex].Name.Buffer, RefDomains->Domains[Names[0].DomainIndex].Name.Length/2, Names[0].Name.Buffer, Names[0].Name.Length/2); } else { // // add only the account name // rc = ScepAddToNameList( pNameList, Names[0].Name.Buffer, Names[0].Name.Length/2); } } else { rc = ERROR_NONE_MAPPED; } } else { // // lookup in the constant table for builtin accounts // note. This will resolve the builtin SIDs to the language of the binary // which may not be the locale the process is running (UI) // for (int i = 0; i < TABLE_SIZE; i++) { if ( _wcsicmp(NameTable[i].StrSid, LookupString+1) == 0 ) { // //found match // rc = ScepAddToNameList( pNameList, NameTable[i].Name, 0); break; } } } if ( ERROR_SUCCESS != rc ) { // // either invalid sid string, or not found a name map, or // failed to add to the name list, just simply add the sid string to the name list // rc = ScepAddToNameList( pNameList, LookupString, Len); } if ( Names ) { LsaFreeMemory(Names); } if ( RefDomains ) { LsaFreeMemory(RefDomains); } return(ScepDosErrorToSceStatus(rc)); } SCESTATUS ScepLookupNameAndAddToSidStringList( IN LSA_HANDLE LsaHandle, IN OUT PSCE_NAME_LIST *pNameList, IN PWSTR LookupString, IN ULONG Len ) { if ( LsaHandle == NULL || LookupString == NULL || Len == 0 ) { return(SCESTATUS_INVALID_PARAMETER); } if ( LookupString[0] == L'\0' ) { return(SCESTATUS_INVALID_PARAMETER); } // // convert the sid string to a real sid // PSID pSid=NULL; NTSTATUS NtStatus; DWORD rc; PLSA_REFERENCED_DOMAIN_LIST RefDomains=NULL; PLSA_TRANSLATED_SID2 Sids=NULL; UNICODE_STRING UnicodeName; NtStatus = ScepLsaLookupNames2( LsaHandle, LSA_LOOKUP_ISOLATED_AS_LOCAL, LookupString, &RefDomains, &Sids ); rc = RtlNtStatusToDosError(NtStatus); if ( ERROR_SUCCESS == rc && Sids ) { // // name is found, make domain\account format // if ( Sids[0].Use != SidTypeInvalid && Sids[0].Use != SidTypeUnknown && Sids[0].Sid ) { // // this name is mapped // convert to a sid string, note: a prefix "*" should be added // UNICODE_STRING UnicodeStringSid; rc = RtlNtStatusToDosError( RtlConvertSidToUnicodeString(&UnicodeStringSid, Sids[0].Sid, TRUE )); if ( ERROR_SUCCESS == rc ) { rc = ScepAddTwoNamesToNameList( pNameList, FALSE, TEXT("*"), 1, UnicodeStringSid.Buffer, UnicodeStringSid.Length/2); RtlFreeUnicodeString( &UnicodeStringSid ); } } else { rc = ERROR_NONE_MAPPED; } } if ( ERROR_SUCCESS != rc ) { // // either invalid sid string, or not found a name map, or // failed to add to the name list, just simply add the sid string to the name list // rc = ScepAddToNameList( pNameList, LookupString, Len); } if ( Sids ) { LsaFreeMemory(Sids); } if ( RefDomains ) { LsaFreeMemory(RefDomains); } return(ScepDosErrorToSceStatus(rc)); } NTSTATUS ScepOpenLsaPolicy( IN ACCESS_MASK access, OUT PLSA_HANDLE pPolicyHandle, IN BOOL bDoNotNotify ) /* ++ Routine Description: This routine opens the LSA policy with the desired access. Arguments: access - the desired access to the policy pPolicyHandle - returned address of the Policy Handle Return value: NTSTATUS -- */ { NTSTATUS NtStatus; LSA_OBJECT_ATTRIBUTES attributes; SECURITY_QUALITY_OF_SERVICE service; memset( &attributes, 0, sizeof(attributes) ); attributes.Length = sizeof(attributes); attributes.SecurityQualityOfService = &service; service.Length = sizeof(service); service.ImpersonationLevel= SecurityImpersonation; service.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING; service.EffectiveOnly = TRUE; // // open the lsa policy first // NtStatus = LsaOpenPolicy( NULL, &attributes, access, pPolicyHandle ); /* if ( NT_SUCCESS(NtStatus) && bDoNotNotify && *pPolicyHandle ) { NtStatus = LsaSetPolicyReplicationHandle(pPolicyHandle); if ( !NT_SUCCESS(NtStatus) ) { LsaClose( *pPolicyHandle ); *pPolicyHandle = NULL; } } */ return(NtStatus); } BOOL ScepIsSidFromAccountDomain( IN PSID pSid ) { if ( pSid == NULL ) { return(FALSE); } if ( !RtlValidSid(pSid) ) { return(FALSE); } PSID_IDENTIFIER_AUTHORITY pia = RtlIdentifierAuthoritySid ( pSid ); if ( pia ) { if ( pia->Value[5] != 5 || pia->Value[0] != 0 || pia->Value[1] != 0 || pia->Value[2] != 0 || pia->Value[3] != 0 || pia->Value[4] != 0 ) { // // this is not a account from account domain // return(FALSE); } if ( RtlSubAuthorityCountSid( pSid ) == 0 || *RtlSubAuthoritySid ( pSid, 0 ) != SECURITY_NT_NON_UNIQUE ) { return(FALSE); } return(TRUE); } return(FALSE); } //+-------------------------------------------------------------------------- // // Function: SetupINFAsUCS2 // // Synopsis: Dumps some UCS-2 to the specified INF file if it // doesn't already exist; this makes the .inf/.ini manipulation code // use UCS-2. // // Arguments: The file to create and dump to // // Returns: 0 == failure, non-zero == success; use GetLastError() // to retrieve the error code (same as WriteFile). // //+-------------------------------------------------------------------------- BOOL SetupINFAsUCS2(LPCTSTR szName) { HANDLE file; BOOL status; file = CreateFile(szName, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL); if (file == INVALID_HANDLE_VALUE) { if (GetLastError() != ERROR_ALREADY_EXISTS) // Well, this isn't good -- we lose. status = FALSE; else // Otherwise, the file already existed, which is just fine. // We'll just let the .inf/.ini manipulation code keep using // the same charset&encoding... status = TRUE; } else { // We created the file -- it didn't exist. // So we need to spew a little UCS-2 into it. static WCHAR str[] = L"0[Unicode]\r\nUnicode=yes\r\n"; DWORD n_written; BYTE *pbStr = (BYTE *)str; pbStr[0] = 0xFF; pbStr[1] = 0xFE; status = WriteFile(file, (LPCVOID)str, sizeof(str) - sizeof(UNICODE_NULL), &n_written, NULL); CloseHandle(file); } return status; } //+-------------------------------------------------------------------------- // // Function: ScepStripPrefix // // Arguments: pwszPath to look in // // Returns: Returns ptr to stripped path (same if no stripping) // //+-------------------------------------------------------------------------- WCHAR * ScepStripPrefix( IN LPTSTR pwszPath ) { WCHAR wszMachPrefix[] = TEXT("LDAP://CN=Machine,"); INT iMachPrefixLen = lstrlen( wszMachPrefix ); WCHAR wszUserPrefix[] = TEXT("LDAP://CN=User,"); INT iUserPrefixLen = lstrlen( wszUserPrefix ); WCHAR *pwszPathSuffix; // // Strip out prefix to get the canonical path to Gpo // if ( CompareString( LOCALE_USER_DEFAULT, NORM_IGNORECASE, pwszPath, iUserPrefixLen, wszUserPrefix, iUserPrefixLen ) == CSTR_EQUAL ) { pwszPathSuffix = pwszPath + iUserPrefixLen; } else if ( CompareString( LOCALE_USER_DEFAULT, NORM_IGNORECASE, pwszPath, iMachPrefixLen, wszMachPrefix, iMachPrefixLen ) == CSTR_EQUAL ) { pwszPathSuffix = pwszPath + iMachPrefixLen; } else pwszPathSuffix = pwszPath; return pwszPathSuffix; } //+-------------------------------------------------------------------------- // // Function: ScepGenerateGuid // // Arguments: out: the guid string // // Returns: Returns guid string (has to be freed outside // //+-------------------------------------------------------------------------- /* DWORD ScepGenerateGuid( OUT PWSTR *ppwszGuid ) { GUID guid; DWORD rc = ERROR_SUCCESS; if (ppwszGuid == NULL) return ERROR_INVALID_PARAMETER; *ppwszGuid = (PWSTR) ScepAlloc(LMEM_ZEROINIT, (MAX_GUID_STRING_LEN + 1) * sizeof(WCHAR)); if (*ppwszGuid) { if (ERROR_SUCCESS == (rc = ScepWbemErrorToDosError(CoCreateGuid( &guid )))) { if (!SCEP_NULL_GUID(guid)) SCEP_GUID_TO_STRING(guid, *ppwszGuid); else { rc = ERROR_INVALID_PARAMETER; } } } else rc = ERROR_NOT_ENOUGH_MEMORY; return rc; } */ SCESTATUS SceInfpGetPrivileges( IN HINF hInf, IN BOOL bLookupAccount, OUT PSCE_PRIVILEGE_ASSIGNMENT *pPrivileges, OUT PSCE_ERROR_LOG_INFO *Errlog OPTIONAL ) /* ++ Description: Get user right assignments from a INF template. If bLookupAccount is set to TRUE, the accounts in user right assignments will be translated to account names (from SID format); else the information is returned in the same way as defined in the template. Arguments: Return Value: -- */ { INFCONTEXT InfLine; SCESTATUS rc=SCESTATUS_SUCCESS; PSCE_PRIVILEGE_ASSIGNMENT pCurRight=NULL; WCHAR Keyname[SCE_KEY_MAX_LENGTH]; PWSTR StrValue=NULL; DWORD DataSize; DWORD PrivValue; DWORD i, cFields; LSA_HANDLE LsaHandle=NULL; // // [Privilege Rights] section // if(SetupFindFirstLine(hInf,szPrivilegeRights,NULL,&InfLine)) { // // open lsa policy handle for sid/name lookup // rc = RtlNtStatusToDosError( ScepOpenLsaPolicy( POLICY_LOOKUP_NAMES | POLICY_VIEW_LOCAL_INFORMATION, &LsaHandle, TRUE )); if ( ERROR_SUCCESS != rc ) { ScepBuildErrorLogInfo( rc, Errlog, SCEERR_ADD, TEXT("LSA") ); return(ScepDosErrorToSceStatus(rc)); } do { memset(Keyname, '\0', SCE_KEY_MAX_LENGTH*sizeof(WCHAR)); rc = SCESTATUS_SUCCESS; if ( SetupGetStringField(&InfLine, 0, Keyname, SCE_KEY_MAX_LENGTH, NULL) ) { // // find a key name (which is a privilege name here ). // lookup privilege's value // if ( ( PrivValue = ScepLookupPrivByName(Keyname) ) == -1 ) { ScepBuildErrorLogInfo( ERROR_INVALID_DATA, Errlog, SCEERR_INVALID_PRIVILEGE, Keyname ); // goto NextLine; } // // a sm_privilege_assignment structure. allocate buffer // pCurRight = (PSCE_PRIVILEGE_ASSIGNMENT)ScepAlloc( LMEM_ZEROINIT, sizeof(SCE_PRIVILEGE_ASSIGNMENT) ); if ( pCurRight == NULL ) { rc = SCESTATUS_NOT_ENOUGH_RESOURCE; goto Done; } pCurRight->Name = (PWSTR)ScepAlloc( (UINT)0, (wcslen(Keyname)+1)*sizeof(WCHAR)); if ( pCurRight->Name == NULL ) { ScepFree(pCurRight); rc = SCESTATUS_NOT_ENOUGH_RESOURCE; goto Done; } wcscpy(pCurRight->Name, Keyname); pCurRight->Value = PrivValue; cFields = SetupGetFieldCount( &InfLine ); for ( i=0; i 1) { StrValue = (PWSTR)ScepAlloc( 0, (DataSize+1)*sizeof(WCHAR) ); if ( StrValue == NULL ) rc = SCESTATUS_NOT_ENOUGH_RESOURCE; else { StrValue[DataSize] = L'\0'; if ( SetupGetStringField( &InfLine, i+1, StrValue, DataSize, NULL) ) { if ( bLookupAccount && StrValue[0] == L'*' && DataSize > 0 ) { // // this is a SID format, should look it up // rc = ScepLookupSidStringAndAddToNameList( LsaHandle, &(pCurRight->AssignedTo), StrValue, // +1, DataSize // -1 ); } else { rc = ScepAddToNameList(&(pCurRight->AssignedTo), StrValue, DataSize ); } } else rc = SCESTATUS_INVALID_DATA; } ScepFree( StrValue ); StrValue = NULL; } } else { ScepBuildErrorLogInfo( ERROR_INVALID_DATA, Errlog, SCEERR_QUERY_INFO, Keyname ); rc = SCESTATUS_INVALID_DATA; } } if ( rc == SCESTATUS_SUCCESS ) { // // add this node to the list // pCurRight->Next = *pPrivileges; *pPrivileges = pCurRight; pCurRight = NULL; } else ScepFreePrivilege(pCurRight); } else rc = SCESTATUS_BAD_FORMAT; //NextLine: if (rc != SCESTATUS_SUCCESS ) { ScepBuildErrorLogInfo( ScepSceStatusToDosError(rc), Errlog, SCEERR_QUERY_INFO, szPrivilegeRights ); goto Done; } } while(SetupFindNextLine(&InfLine,&InfLine)); } Done: if ( StrValue != NULL ) ScepFree(StrValue); if ( LsaHandle ) { LsaClose(LsaHandle); } return(rc); } NTSTATUS ScepIsSystemContext( IN HANDLE hUserToken, OUT BOOL *pbSystem ) { NTSTATUS NtStatus; DWORD nRequired; // // variables to determine calling context // PTOKEN_USER pUser=NULL; SID_IDENTIFIER_AUTHORITY ia=SECURITY_NT_AUTHORITY; PSID SystemSid=NULL; BOOL b; // // get current user SID in the token // NtStatus = NtQueryInformationToken (hUserToken, TokenUser, NULL, 0, &nRequired ); if ( STATUS_BUFFER_TOO_SMALL == NtStatus ) { pUser = (PTOKEN_USER)LocalAlloc(0,nRequired+1); if ( pUser ) { NtStatus = NtQueryInformationToken (hUserToken, TokenUser, (PVOID)pUser, nRequired, &nRequired ); } else { NtStatus = STATUS_NO_MEMORY; } } b = FALSE; if ( NT_SUCCESS(NtStatus) && pUser && pUser->User.Sid ) { // // build system sid and compare with the current user SID // NtStatus = RtlAllocateAndInitializeSid (&ia,1,SECURITY_LOCAL_SYSTEM_RID, 0, 0, 0, 0, 0, 0, 0, &SystemSid); if ( NT_SUCCESS(NtStatus) && SystemSid ) { // // check to see if it is system sid // if ( RtlEqualSid(pUser->User.Sid, SystemSid) ) { b=TRUE; } } } // // free memory allocated // if ( SystemSid ) { FreeSid(SystemSid); } if ( pUser ) { LocalFree(pUser); } *pbSystem = b; return NtStatus; } BOOL IsNT5() { WCHAR szInfName[MAX_PATH*2+1]; szInfName[0] = L'\0'; DWORD cNumCharsReturned = GetSystemWindowsDirectory(szInfName, MAX_PATH); if (cNumCharsReturned) { wcscat(szInfName, L"\\system32\\$winnt$.inf"); } else { return TRUE; } UINT nRet = GetPrivateProfileInt( L"Networking", L"BuildNumber", 0, szInfName ); if (nRet == 0) { return TRUE; } else if (nRet > 1381) { return TRUE; } return FALSE; } DWORD ScepVerifyTemplateName( IN PWSTR InfTemplateName, OUT PSCE_ERROR_LOG_INFO *pErrlog OPTIONAL ) /* Routine Description: This routine verifies the template name for read protection and invalid path Arguments: InfTemplateName - the full path name of the inf template pErrlog - the error log buffer Return Value: WIN32 error code */ { if ( !InfTemplateName ) { return(ERROR_INVALID_PARAMETER); } PWSTR DefProfile; DWORD rc; // // verify the InfTemplateName to generate // if read only, or access denied, return ERROR_ACCESS_DENIED // if invalid path, return ERROR_PATH_NOT_FOUND // DefProfile = InfTemplateName + wcslen(InfTemplateName)-1; while ( DefProfile > InfTemplateName+1 ) { if ( *DefProfile != L'\\') { DefProfile--; } else { break; } } rc = NO_ERROR; if ( DefProfile > InfTemplateName+2 ) { // at least allow a drive letter, a colon, and a \ // // find the directory path // DWORD Len=(DWORD)(DefProfile-InfTemplateName); PWSTR TmpBuf=(PWSTR)LocalAlloc(0, (Len+1)*sizeof(WCHAR)); if ( TmpBuf ) { wcsncpy(TmpBuf, InfTemplateName, Len); TmpBuf[Len] = L'\0'; if ( 0xFFFFFFFF == GetFileAttributes(TmpBuf) ) rc = ERROR_PATH_NOT_FOUND; LocalFree(TmpBuf); } else { rc = ERROR_NOT_ENOUGH_MEMORY; } } else if ( DefProfile == InfTemplateName+2 && InfTemplateName[1] == L':' ) { // // this is a template path off the root // } else { // // invalid directory path // rc = ERROR_PATH_NOT_FOUND; } if ( rc != NO_ERROR ) { // // error occurs // if ( ERROR_PATH_NOT_FOUND == rc ) { ScepBuildErrorLogInfo( rc, pErrlog, SCEERR_INVALID_PATH, InfTemplateName ); } return(rc); } // // make it unicode aware // do not worry about failure // SetupINFAsUCS2(InfTemplateName); // // validate if the template is write protected // FILE *hTempFile; hTempFile = _wfopen(InfTemplateName, L"a+"); if ( !hTempFile ) { // // can't overwrite/create the file, must be access denied // rc = ERROR_ACCESS_DENIED; ScepBuildErrorLogInfo( rc, pErrlog, SCEERR_ERROR_CREATE, InfTemplateName ); return(rc); } else { fclose( hTempFile ); } return(rc); }