Storing Secrets in Windows 2000 and Windows XP

Storing Secrets in Windows 2000 and Windows XP

When storing secret data for a user of Windows 2000 or Windows XP, you should use the Data Protection API (DPAPI) functions CryptProtectData and CryptUnprotectData. These functions encrypt and decrypt data by using a key derived from the user s password. In addition, decryption can be done only on the computer where the data was encrypted unless the user has a roaming profile, in which case she can decrypt the data from another computer on the network. There are a couple of catches, however. The keys for decrypting this information are stored in the roaming profile and are based in part on the user s password. This has several implications. One is that the user cannot encrypt data in the roaming profile itself by using the Encrypting File System (EFS). Thus, obviously, your application cannot rely on information stored in the user s documents folder being encrypted with EFS, nor can your application require it. Second, because the user s password provides input into the encryption of the secret keys, if an administrator resets the local user s password, all the keys are lost. Windows XP has functionality to recover those keys by means of the password recovery disk, but those disks must be jealously guarded. In addition, the backup copies of the keys are encrypted using the master key of the domain controller that authenticated the user. If that domain controller is no longer available when the backup keys are needed, it will not be possible to access those backup keys. Note that this scenario does not apply to domain accounts.

CryptProtectData also adds an integrity check called a message authentication code (MAC) to the encrypted data to detect data tampering. Finally, the data is ACLed such that only the user account that encrypted the data can decrypt the data.

important

Any data protected by DPAPI, and potentially any protection mechanism, is accessible by any code you run. If you can read the data, any code that runs as you can read the data also. The moral of the story is, don t run code you don t trust.

While this is discouraged on Windows 2000 and Windows XP, you can also use the Local Security Authority (LSA) secrets APIs, LsaStorePrivateData and LsaRetrievePrivateData, if your process is running with high privileges or as SYSTEM.

LSA secrets are discouraged on Windows 2000 and Windows XP because LSA will store only a total of 4096 secrets per system. 2048 are reserved by the operating system for its own use, leaving 2048 for nonsystem use. As you can see, secrets are a scarce resource. Use DPAPI instead. I ll cover LSA secrets in detail later in this chapter.

The following code sample shows how to store and retrieve data by using DPAPI functions. You can also find this example code on the companion CD in the folder Secureco\Chapter 7\DPAPI.

 // Data to protect DATA_BLOB blobIn; blobIn.pbData = reinterpret_cast<BYTE *>("This is my secret data."; blobIn.cbData = lstrlen(reinterpret_cast<char *>(blobIn.pbData))+1; // Optional entropy via an external function call DATA_BLOB blobEntropy; blobEntropy.pbData = GetEntropyFromUser(); blobEntropy.cbData = lstrlen( reinterpret_cast<char *>(blobEntropy.pbData)); // Encrypt the data. DATA_BLOB blobOut; if(CryptProtectData( &blobIn, L"Writing Secure Code Example", &blobEntropy, NULL, NULL, 0, &blobOut)) { printf("Protection worked.\n"); } else { printf("Error calling CryptProtectData() -> %x", GetLastError()); exit(-1); } // Decrypt the data. DATA_BLOB blobVerify; if (CryptUnprotectData( &blobOut, NULL, &blobEntropy, NULL, NULL, 0, &blobVerify)) { printf("The decrypted data is: %s\n", blobVerify.pbData); } else { printf("Error calling CryptUnprotectData() -> %x", GetLastError()); exit(-1); } LocalFree(blobOut.pbData); LocalFree(blobVerify.pbData);

A Special Case: Client Credentials in Windows XP

Windows XP includes functionality named Stored User Names And Passwords to make handling users passwords and other credentials, such as private keys, easier, more consistent, and safer. If your application includes a client component that requires you to prompt for or store a user s credentials, you should seriously consider using this feature for the following reasons:

  • Support for different types of credentials, such as passwords and keys, on smart cards.

  • Support for securely saving credentials by using DPAPI.

  • No need to define your own user interface. It s provided, although you can add a custom image to the dialog box.

Stored User Names And Passwords can handle two types of credentials: Windows domain credentials and generic credentials. Domain credentials are used by portions of the operating system and can be retrieved only by an authentication package, such as Kerberos. If you write your own Security Support Provider Interface (SSPI), you can use domain credentials also. Generic credentials are application-specific and apply to applications that maintain their own authentication and authorization mechanisms for example, an accounting package that uses its own lookup SQL database for security data.

The following sample code shows how to prompt for generic credentials:

/* Cred.cpp */ #include <stdio.h> #include <windows.h> #include <wincred.h> CREDUI_INFO cui; cui.cbSize = sizeof CREDUI_INFO; cui.hwndParent = NULL; cui.pszMessageText = TEXT("Please Enter your Northwind Traders Accounts password."); cui.pszCaptionText = TEXT("Northwind Traders Accounts"); cui.hbmBanner = NULL; PCTSTR pszTargetName = TEXT("NorthwindAccountsServer"); DWORD dwErrReason = 0; Char pszName[CREDUI_MAX_USERNAME_LENGTH+1]; Char pszPwd[CREDUI_MAX_PASSWORD_LENGTH+1]; DWORD dwName = CREDUI_MAX_USERNAME_LENGTH; DWORD dwPwd = CREDUI_MAX_PASSWORD_LENGTH; BOOL fSave = FALSE; DWORD dwFlags = CREDUI_FLAGS_GENERIC_CREDENTIALS CREDUI_FLAGS_ALWAYS_SHOW_UI; // Zero out username and password, as they are [in,out] parameters. ZeroMemory(pszName, dwName); ZeroMemory(pszPwd, dwPwd); DWORD err = CredUIPromptForCredentials( &cui, pszTargetName, NULL, dwErrReason, pszName,dwName, pszPwd,dwPwd, &fSave, dwFlags); if (err) printf("CredUIPromptForCredentials() failed -> %d", GetLastError()); else { // Access the Northwind Traders Accounting package using // pszName and pszPwd over a secure channel. }

You can also find this example code on the companion CD in the folder Secureco\Chapter 7\Cred. This code produces the dialog box in Figure 7-1. Note that the username and password are prepopulated if the credentials are already stored for the target in this case, NorthwindAccountsServer and that the credentials are cached in DPAPI.

Figure 7-1

A Credential Manager dialog box with a prepopulated username and password.

You can also use a command line specific function that does not pop up a dialog box: CredUICmdLinePromptForCredentials.

Finally, if the credential user interface functions are not flexible enough for your application, there are a range of low-level functions documented in the Platform SDK that should meet your needs.

It is highly recommended that you use the Stored User Names And Passwords functionality when accepting credentials from users accessing your application because it provides a simple and consistent interface.



Writing Secure Code
Writing Secure Code, Second Edition
ISBN: 0735617228
EAN: 2147483647
Year: 2005
Pages: 153

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net