Security Features in Windows ce

Security Features in Windows CE
Marcus Ash and Mukkul Dasgupta
Microsoft Corporation
Originally published January, 2003
Create a Trusted Environment
Microsoft Windows CE devices send, receive and process information that requires protection from potentially unsafe applications. To help protect your device, you can implement security measures that can help prevent the OS from loading unknown modules, restrict access to system application programming interfaces (APIs), and prevent write access to parts of the system registry. You can designate a module as trusted or not trusted when certifying applications. The kernel can use this information to prevent unauthorized applications from loading or limit their access to the system.
To help create a trusted environment, you must implement the following functions:
OEMCertifyModuleInit
OEMCertifyModule
Before the kernel loads an application, the OEMCertifyModule function verifies the application signature to help protect your system from unfamiliar applications. This ensures that the Windows CE based platform loads an application only if it contains a valid digital signature. The following table further describes the functions.
Function
|
Description
|
Return value
|
OEMCertifyModuleInit
|
Enables the OS loader to notify the OEM that a new module is being loaded. Allows the OEM to decide whether to verify the module for safety.
|
TRUE or FALSE
|
OEMCertifyModule
|
Allows the OS loader to pass the module code (for example, DLL, EXE and OCX) to the OEM for verification that it is safe to run on the system.
|
OEM_CERTIFY_TRUST
OEM_CERTIFY_RUN
OEM_CERTIFY_FALSE
|
The following table describes the return values for OEMCertifyModule function.
Return value
|
Description
|
OEM_CERTIFY_TRUST
|
Fully trusted application to perform any operation
|
OEM_CERTIFY_RUN
|
Trusted application to run, but restricted from making any privileged function calls
|
OEM_CERTIFY_FALSE
|
Not trusted and therefore not allowed to run
|
An OEMCertifyModule function can perform any type of check on the module that is loading. For example, the function may perform a cyclic redundancy check or a certificate check. When a dynamic-link library (DLL) loads into the address space of an .exe file, the trust level of the .exe file determines the final access level. For example, when an .exe file with the OEM_CERTIFY_RUN trust level tries to load a DLL that has a higher trust level (OEM_CERTIFY_TRUST), the final trust level of the DLL is OEM_CERTIFY_RUN. On the other hand, when the .exe file tries to load a DLL that has a lower trust level, the DLL will fail to load.
The following table shows the different combinations of .exe file and DLL trust levels.
EXE trust
|
DLL trust
|
Final DLL trust
|
OEM_CERTIFY_RUN
|
OEM_CERTIFY_RUN
|
OEM_CERTIFY_RUN
|
OEM_CERTIFY_RUN
|
OEM_CERTIFY_TRUST
|
OEM_CERTIFY_RUN
|
OEM_CERTIFY_TRUST
|
OEM_CERTIFY_RUN
|
DLL fails to load
|
OEM_CERTIFY_TRUST
|
OEM_CERTIFY_TRUST
|
OEM_CERTIFY_TRUST
|
Note: When you implement the trusted security model, untrusted drivers will fail to load. The OEM must verify all third-party drivers as trusted.
The simplest implementation of the trusted model uses the OEMCertifyModule function to return OEM_CERTIFY_RUN for all applications. This allows applications that are not part of the ROM MODULES section of the image to run, but they are restricted from making privileged function calls. By using the OEMCertifyModule implementation at run time, you do not have to specify which applications are trusted. If OEM_CERTIFY_FALSE is returned, applications in RAM will not be able to run. Regardless of which implementation of the trusted model you choose, OEMCertifyModuleInit or OEMCertifyModule, the operating system files in the ROM MODULES section of the image will always run with full privileges.
See also:
OEMCertifyModuleInit
OEMCertifyModule
Creating a signature
To create a digital signature from a file, run the file through a hash function and then sign the resulting hash with a private key. An easy way to create a digital signature from a file is to use Signfile.exe, which is included in Microsoft Platform Builder. Signfile.exe is a tool for signing an executable file with a private key supplied by a cryptographic service provider (CSP).
Signfile.exe uses the Secure Hashing Algorithm (SHA) to compute the cryptographic hash. SHA generates a 20-byte hash from an arbitrarily sized byte string. Signfile.exe pads the hash as specified by Public-Key Cryptography Standards #1 (PKCS1) and encrypts it by using the RSA public key algorithm. The key modulus length can be from 512 through 1,024 bits. The resulting signature is the same size as the modulus. For example, the signature for a 1,024 bit key is 128 bytes. Signfile.exe then uses the ImageAddCertificate and ImageGetDigestStream Microsoft Windows NT functions to embed the signature in a portable executable (PE) file.
The following list shows the contents of the PE file memory:
Microsoft MS-DOS header
Offset of PE header (offset 0x3c)
PE header
Section headers
Section
Debug information and certificates (if any)
The PE header begins with a 4-byte sequence, "PE\0\0", which identifies the MS-DOS header. The MS-DOS header is followed by a standard Common Object File Format (COFF) header. This COFF header is followed by an optional header that is always present on Windows .exe and .dll files. The last field in a PE header is an optional data directory table. The following table shows the size of the s.
PE header element
|
Size
|
"PE\0\0"
|
4 bytes
|
COFF header
|
20 bytes
|
Optional header; standard for Windows files
|
96 bytes
|
Optional header; data directory table
|
Size varies
|
Each entry in the data directory table consists of an IMAGE_DATA_DIRECTORY structure. The fifth structure in the data directory table contains certificate table information. This is stored in an array of WIN_CERTIFICATE structures. A certificate is a digitally signed statement that contains information about an entity and that entity's public key. Certificates are not loaded into memory as part of the PE file.
The following code example shows the format of a WIN_CERTIFICATE structure that is needed to support the PKCS1 standard.
Listing
typedef struct {
// Standard WIN_CERTIFICATE fields (8 bytes)
DWORD dwLength;
WORD wRevision;
WORD wCertificateType; // = WIN_CERT_TYPE_PKCS1_SIGN
// WIN_CERT_TYPE_PKCS1_SIGN fields follow
DWORD cbSignedData;
// optional signed attributes
BYTE bSignedData[MAX_WIN_CERT_SIGN_DATA_LEN];
BYTE bSign[MAX_RSA_KEY_BITS/8]; // PKCS1 signature
} PKCS1_MODULE_SIGN ;
Signfile.exe appends the WIN_CERTIFICATE structure to the end of the file and updates the file header accordingly. For sample Signfile.exe code, see %_WINCEROOT%\Public\Common\Oak\Tools\Signfile.
Verifying a signature
An OEM can verify that a file contains the proper signature by using the OEMCertifyModule function before the kernel loads the file.
Using this method, the kernel uses the same hash formula to calculate a signature during the verification process that it used during the signature generation process. OEMCertifyModule compares the signature it calculated from the hash to the signature in the file. If the signatures do not match, OEMCertifyModule prevents the file from loading.
Because the size of a PE file header can vary, OEMCertifyModule may not be able to process the entire header in a single call. Because the signature is at the end of the file, it is not possible to specify a hash function in the signature; you must use Secure Hash Algorithm (SHA) or another fixed hash function. When computing the hash, SHA excludes the following data from the file:
Checksum field in the Windows COFF header (4 bytes)
Certificate data directory structure (8 bytes)
WIN_CERTIFICATE structure (size varies)
You can either write the code that is necessary to implement the signature verification and signing tool yourself or use the sample verification library Loadauth.lib. This is included in Platform Builder in the processor-specific directory under %_WINCEROOT%\Public\Common\Oak\Lib, and the signing tool Signfile.exe.
The following table shows the functions contained in Loadauth.lib library.
Function
|
Description
|
InitPubKey
|
Initializes a public key to be used for signature verification
|
CertifyModuleInit
|
Initiates the process of verifying a signature on a module for the purpose of certifying the module
|
CertifyModule
|
Streams the bytes of a module for certification
|
CertifyModuleFinal
|
Returns the final certification status of a file and any data embedded in the signature
|
OEMCertifyModuleInit_and_OEMCertifyModule'>To use the sample verification library (Loadauth.lib)
Add OEMCertifyModuleInit and OEMCertifyModule to the OEM adaptation layer (OAL) and initialize the function pointers.
Create and export a hard-coded public key. The key must have a PUBLICKEYBLOB format. You can use the Signfile.exe tool to complete this step.
Incorporate the public key into the operating system (OS) image. You can do this by modifying the OAL according to the sample code in %_WINCEROOT%\Platform\%BSP%\Kernel\Hal.
The following code example shows an implementation of the OEMCertifyModuleInit and OEMCertifyModule functions using Loadauth.lib functions.
Listing
/* The signature public key BLOB */
const unsigned char g_bSignPublicKeyBlob[] = {
0x06,0x02,0x00,0x00,0x00,0x24,0x00,0x00,0x52,0x53,0x41,0x31,0x00,0x02,
0x00,0x00,0x01,0x00,0x01,0x00,0xb1,0x00,0x93,0x7c,0x18,0x63,0xce,0xf3,
0x23,0xe3,0x57,0x74,0x13,0x54,0x17,0x2c,0xdb,0xf6,0x56,0x77,0xb3,0x8d,
0x34,0x6c,0x41,0x3d,0x4e,0xbb,0xc1,0xaf,0x3d,0x17,0xb6,0x0e,0x70,0x72,
0x43,0x12,0x1d,0xb1,0x2a,0x57,0x05,0x27,0x58,0x63,0xef,0xb7,0x3b,0x71,
0xee,0xe4,0xcd,0x14,0xbe,0xf7,0x32,0xec,0xa2,0xae,0xbf,0x9a,0x6b,0x75
};
// Declarations for load-time signature checking
// of RAM executables
typedef BOOL (* OEMLoadInit_t)(LPWSTR lpszName);
typedef DWORD (* OEMLoadModule_t)(LPBYTE lpData, DWORD cbData);
extern OEMLoadInit_t pOEMLoadInit;
extern OEMLoadModule_t pOEMLoadModule;
extern BOOL InitPubKey(const BYTE *KeyBlob, DWORD cbKeyBlob);
// Loadauth library routines
extern BOOL CertifyModuleInit(void);
extern BOOL CertifyModule(PBYTE pbBlock, DWORD cbBlock);
extern BOOL CertifyModuleFinal(PBYTE *ppbSignData,
PDWORD pcbSignData);
// Called once for each RAM executable module
// to initialize signature checking
static BOOL OEMCertifyInit(LPWSTR lpszName)
{
return CertifyModuleInit();
}
// called one or more times after OemLoadInit
static DWORD OEMCertifyModule(LPBYTE lpData, DWORD cbData)
{
if (cbData)
{
// process module bytes
return CertifyModule(lpData, cbData);
}
else
{
// final call
DWORD dwTrustLevel = OEM_CERTIFY_FALSE;
LPBYTE pSignedData;
DWORD cbSignedData;
BOOL fRet = CertifyModuleFinal(&pSignedData,
&cbSignedData);
if (fRet)
{
// the file has a valid signature
// we expect the trust level to be returned as
// signed data
if (cbSignedData < sizeof(CHAR))
dwTrustLevel = OEM_CERTIFY_RUN;
else
switch (*pSignedData)
{
case 'T' :
dwTrustLevel = OEM_CERTIFY_TRUST;
break;
case 'R' :
dwTrustLevel = OEM_CERTIFY_RUN;
break;
default:
dwTrustLevel = OEM_CERTIFY_FALSE;
break;
}
}
#ifdef DEBUG
if (!fRet)
lpWriteDebugStringFunc(TEXT("OEMCertifyModule:signature
check failed.\r\n"));
#endif
// return one of the OEM_CERTIFY levels
return dwTrustLevel;
}
}
void OEMInit()
{
...
...
//
// Set the module signature verification hooks.
//
pOEMLoadInit = OEMCertifyInit;
pOEMLoadModule = OEMCertifyModule;
//
// Initialize the signature verification public key.
//
InitPubKey(g_bSignPublicKeyBlob,sizeof(g_bSignPublicKeyBlob));
//
// Other OEM initialization steps
//
...
}
Note: You can use any name for the functions OEMCertifyModuleInit and OEMCertifyModule. However, it is important to initialize the two kernel pointers, pOEMLoadInit and pOEMLoadModule, in the OEMInit function to these named functions.
Signfile.exe
Signfile.exe signs an executable with a supplied private key.
You can use the following command-line parameters with this tool:
signfile [ parameters ]
Parameters
-f PEFile
Specifies the file to be signed.
-a
Appends signature to PE File.
-k KeyName
Uses a private key from the named CryptoAPI key container.
-p Cfile
Outputs the public key to a file as a C structure.
-s AttribString
Specifies an optional attribute string to be included in signature. For example, you can add a string to indicate the trust level of the application.
-p SignFile
Outputs the signature to a file.
The following command-line example shows how to sign Xyz.dll by using the private key in key container TESTKEY1024.
Signfile -fXyz.dll -a -kTESTKEY1024
For more information about this tool, see Creating Digital Signatures.
Trusted APIs
In addition to the OEM functions, the CeGetCurrentTrust and CeGetCallerTrust APIs enable a DLL to query the trust level of a calling application. You can use these functions to verify the trust levels of the applications.
The following list shows the application programming interfaces (APIs) that can be called only by trusted applications:
AllocPhysMem
CeSetThreadPriority
CeSetThreadQuantum
CheckPassword
ContinueDebugEvent
CryptUnprotectData
DebugActiveProcess
ForcePageout
FreeIntChainHandler
FreePhysMem
InterruptDisable
InterruptDone
InterruptInitialize
KernelLibIoControl
LoadDriver
LoadIntChainHandler
LoadKernelLibrary
LockPages
PowerOffSystem
ReadProcessMemory
ReadRegistryFromOEM
RegCopyFile
RegReplaceKey
RegRestoreFile
RegSaveKey
SetCleanRebootFlag
SetCurrentUser
SetInterruptEvent
SetKMode
SetPassword
SetPasswordStatus
SetProcPermissions
SetSystemMemoryDivision
SetUserData
UnlockPages
VirtualCopy
VirtualSetPageFlags
WaitForDebugEvent
WriteProcessMemory
WriteRegistryToOEM
The following list shows file-based APIs that are influenced by the SYSTEM attribute that can be set on a file.
CopyFile
CreateFile
CreateFileForMapping
DeleteAndRenameFile
DeleteFile
MoveFile
RemoveDirectory
SetFileAttributes
For more information, see File System Security.
The following list shows database APIs that are influenced by the SYSTEM attribute that can be set on a database.
CeCreateDatabaseEx2
CeDeleteDatabaseEx
CeMountDBVol
CeOpenDatabaseEx2
CeSetDatabaseInfoEx2
For more information, see Database Security.
In addition, the debug flags DEBUG_ONLY_THIS_PROCESS and DEBUG_PROCESS of the CreateProcess API are restricted. If these flags are used by a non-trusted application, the identified process will still launch but no debugging will occur.
Debug flags, DEBUG_ONLY_THIS_PROCESS and DEBUG_PROCESS, in the CreateProcess API are also restricted.
The registry architecture in Windows CE is designed to allow only trusted applications that you have identified to modify keys and values in protected portions of the registry.
Because most of the registry is unprotected, OEMs must place all important registry information in one of the protected keys.
Note: All applications have read-only access to all registry keys and values.
In Windows CE, the following registry root keys and their subkeys are protected from untrusted applications:
HKEY_LOCAL_MACHINE\Comm
HKEY_LOCAL_MACHINE\Drivers
HKEY_LOCAL_MACHINE\HARDWARE
HKEY_LOCAL_MACHINE\Init
HKEY_LOCAL_MACHINE\Services
HKEY_LOCAL_MACHINE\SYSTEM
HKEY_LOCAL_MACHINE\WDMDrivers
Untrusted applications are also not allowed to modify protected data. They receive the ERROR_ACCESS_DENIED return value if they attempt to use the following registry functions:
RegSetValueEx
RegCreateKeyEx
RegDeleteKey
RegDeleteValue
See also:
Protected Registry Keys and Values
Database Security
Core OS Interface
Object store security
The object store provides several elements of security in a trusted environment. System files are protected so that they cannot be read or modified by untrusted applications. System files are files that have the system attribute set. For more information, see File System Security.
The system also protects a set of registry keys so that they cannot be modified by untrusted applications. All applications can read all registry keys and values, but only trusted applications can modify values or subkeys below protected keys. The system protects a base set of keys. This set of keys is extensible by the OEM. For more information, see Protected Registry Keys and Values and Request Additional Secure Registry Keys.
Additionally, databases that are stored within the object store can be given a system flag. System databases cannot be read or modified by untrusted applications. Databases that are stored in separate database volumes rather than in the object store can be protected by setting the system attribute on the file, just as for any other file in the file system.
Database security
Windows CE allows trusted applications to mark a system flag on databases to deny access to untrusted callers. Untrusted applications cannot open, read, or modify databases that are marked with the system flag. Trusted callers can set the CEDB_SYSTEMDB flag inside the CEDBASEINFOEX structure passed to CeCreateDatabaseEx2 or CeSetDatabaseInfoEx2 to help protect a database.
This feature helps protect a single database, not an entire database volume. Setting FILE_ATTRIBUTE_SYSTEM on the volume file helps protect database volumes. System databases cannot be created inside database volumes that do not have FILE_ATTRIBUTE_SYSTEM set to block untrusted applications from accessing and/or deleting a file containing a system database using the Microsoft Win32 file APIs. Because an untrusted application cannot access any file with the system file attribute set, adding the system flag to a database inside a database volume does not give it any additional security. Therefore, this feature is most useful in databases that are stored within the object store. Removing the system file attribute from a database volume that contains a system database will expose that database to access by untrusted applications and is not recommended.
Secure Your Communications Network
Security for communications requires special attention because the network interface provides an access point to a device that can be used remotely by an attacker. It is easier for an attacker to remain anonymous or undetected through a network-based attack. This makes determining the source of the attack more difficult.
In general, client applications on a network are relatively more secure than servers or service applications. Clients initiate contacts with specific servers and specify the nature of their requests. This allows the client applications to determine the nature of incoming data and the identity of the server or service; they can reject unsolicited communications. Although Clients are not immune to security problems, they have more control over the nature of communications because they initiate communications, which reduces the surface of vulnerability. Examples of client applications are browsers, email clients and FTP clients.
Servers are more exposed because they wait to receive requests from clients on the network. Requests can come from anywhere in the network. When the server is exposed to the public interface, the surface vulnerability increases considerably. Examples of server applications include Web servers, FTP servers and Telnet servers.
The following list describes the mitigations techniques you can use:
Authentication. When setting up authentication, you should consider whether it is important to authenticate the client to the server, the server to the client, or both. For example, when you connect to a bank, you need to verify the identity of the entity that you are giving credentials to. In this case, you need mutual authentication. On the other hand, when you browse a Web site to get information, you may not care about the identity the entity that is providing the information.
When considering authentication methods, you should be aware that some methods are more vulnerable than others. For example, some methods pass user names and passwords in clear text, which allows anyone who is monitoring the communication to intercept user credentials.
For more information about authentication, see Authentication Services.
Tamper-resistant and privacy-enhanced technologies. To help protect data and other assets from being accessed, changed, and deleted, you can use Secure Sockets Layer (SSL) protocol. It encrypts data as it travels between the client and the server and it uses message authentication codes to provide data integrity.
For more information, see Use SSL to Enhance Security of Network Communication.
Limited access to services and data. To help protect data and other assets, you can use access control lists with COM or Web servers to identify the users and determine the access permissions to resources or services. Many server applications offer their own form of control mechanism.
Encryption. To enhance privacy and integrity of data, you can use CryptoAPI. This provides services that enable data encryption/decryption schemes, authentication using digital signatures, and encoding/decoding to and from ASN.1 to Microsoft Win32-based applications.
For more information, see Microsoft Cryptographic System.
Isolated process and exception handling to provide stability and availability of services. Make sure that your servers or service applications handle process or memory failures gracefully by using useful error messages. Malicious attackers may cause your application to fail or they may tie up network services by flooding the device with too many requests or by sending huge files. For example, Message Queuing (MSMQ) rejects SOAP-based messages sent through HTTP when the message sizes exceed the limits defined in the registry. MSMQ sends an error message when a message is rejected. The buffer size can be optimized for specific applications through the registry.
You can provide stability for your application by terminating a service before the device resources are consumed. For example, Universal Plug and Play (UPnP) limits the number of subscribers to the service and rejects new subscriptions when the maximum number is reached. The subscriber limit can be optimized for specific applications through the registry.
|