/*++ Copyright (c) 1996, 1997 Microsoft Corporation Module Name: crypt32.cpp Abstract: This module contains routines associated with server side Crypt32 operations. Author: Scott Field (sfield) 14-Aug-97 --*/ #include #pragma hdrstop #include #define SECURITY_WIN32 #include #define CRYPTPROTECT_SVR_VERSION_1 0x01 DWORD CPSCreateServerContext( PCRYPT_SERVER_CONTEXT pServerContext, handle_t hBinding ); DWORD CPSDeleteServerContext( PCRYPT_SERVER_CONTEXT pServerContext ); GUID g_guidDefaultProvider = CRYPTPROTECT_DEFAULT_PROVIDER; // // routines to initialize and destroy server state associated with // server callbacks and performance improvements. // DWORD CPSCreateServerContext( PCRYPT_SERVER_CONTEXT pServerContext, handle_t hBinding ) { DWORD dwLastError = ERROR_SUCCESS; ZeroMemory( pServerContext, sizeof(CRYPT_SERVER_CONTEXT) ); pServerContext->cbSize = sizeof(CRYPT_SERVER_CONTEXT); pServerContext->hBinding = hBinding; pServerContext->fImpersonating = FALSE; if(NULL != hBinding) { dwLastError = RpcImpersonateClient( hBinding ); if(ERROR_SUCCESS != dwLastError) { return dwLastError; } } // // Grab the thread token. // if(OpenThreadToken( GetCurrentThread(), TOKEN_QUERY | TOKEN_IMPERSONATE | TOKEN_DUPLICATE, TRUE, &pServerContext->hToken )) { pServerContext->fImpersonating = (NULL == hBinding); } else { HANDLE hProcessToken = NULL; if(OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY | TOKEN_IMPERSONATE | TOKEN_DUPLICATE, &hProcessToken)) { if(!DuplicateTokenEx(hProcessToken, 0, NULL, SecurityImpersonation, TokenImpersonation, &pServerContext->hToken)) { dwLastError = GetLastError(); } CloseHandle(hProcessToken); } else { dwLastError = GetLastError(); } } if(hBinding) { DWORD rc; rc = RpcRevertToSelfEx( hBinding ); if (rc != RPC_S_OK) { if (ERROR_SUCCESS == dwLastError) { dwLastError = rc; } } } // // Is this call from one of the well-known accounts? // { WCHAR szUserName[MAX_PATH + 1]; DWORD cchUserName = MAX_PATH; if(GetUserTextualSid( pServerContext->hToken, szUserName, &cchUserName)) { if(lstrcmpW(szUserName, TEXTUAL_SID_LOCAL_SYSTEM) == 0) { pServerContext->WellKnownAccount = DP_ACCOUNT_LOCAL_SYSTEM; } else if(lstrcmpW(szUserName, TEXTUAL_SID_LOCAL_SERVICE) == 0) { pServerContext->WellKnownAccount = DP_ACCOUNT_LOCAL_SERVICE; } else if(lstrcmpW(szUserName, TEXTUAL_SID_NETWORK_SERVICE) == 0) { pServerContext->WellKnownAccount = DP_ACCOUNT_NETWORK_SERVICE; } } } return dwLastError; } DWORD CPSDeleteServerContext( PCRYPT_SERVER_CONTEXT pServerContext ) { if(pServerContext->szUserStorageArea) { SSFree(pServerContext->szUserStorageArea); pServerContext->szUserStorageArea = NULL; } if(pServerContext->hToken) { CloseHandle(pServerContext->hToken); } if(pServerContext->cbSize == sizeof(CRYPT_SERVER_CONTEXT)) ZeroMemory( pServerContext, sizeof(CRYPT_SERVER_CONTEXT) ); return ERROR_SUCCESS; } DWORD WINAPI CPSDuplicateContext( IN PVOID pvContext, IN OUT PVOID *ppvDuplicateContext ) /*++ Duplicate an outstanding server context so that a provider may defer processing associated with the outstanding context until a later time. This is used to support asynchronous operations on behalf of the caller to the Data Protection API. The caller MUST be impersonating the security context of the client user prior to making this call. --*/ { PCRYPT_SERVER_CONTEXT pServerContext = (PCRYPT_SERVER_CONTEXT)pvContext; PCRYPT_SERVER_CONTEXT pNewContext = NULL; HANDLE hToken = NULL; HANDLE hDuplicateToken; BOOL fSuccess = FALSE; DWORD dwLastError = ERROR_SUCCESS; if( pServerContext == NULL || pServerContext->cbSize != sizeof(CRYPT_SERVER_CONTEXT) || ppvDuplicateContext == NULL ) { return ERROR_INVALID_PARAMETER; } pNewContext = (PCRYPT_SERVER_CONTEXT)SSAlloc( sizeof( CRYPT_SERVER_CONTEXT ) ); if( pNewContext == NULL ) return ERROR_NOT_ENOUGH_SERVER_MEMORY; CopyMemory( pNewContext, pServerContext, sizeof(CRYPT_SERVER_CONTEXT) ); pNewContext->hBinding = NULL; if(pServerContext->szUserStorageArea) { pNewContext->szUserStorageArea = (LPWSTR)SSAlloc((wcslen(pServerContext->szUserStorageArea)+1)* sizeof(WCHAR)); if(NULL != pNewContext->szUserStorageArea) { wcscpy(pNewContext->szUserStorageArea, pServerContext->szUserStorageArea); } } fSuccess = DuplicateTokenEx(pServerContext->hToken, 0, NULL, SecurityImpersonation, TokenImpersonation, &pNewContext->hToken); if( !fSuccess ) { dwLastError = GetLastError(); pNewContext->hToken = NULL; CPSFreeContext( pNewContext ); } else { *ppvDuplicateContext = pNewContext; } return dwLastError; } DWORD WINAPI CPSFreeContext( IN PVOID pvDuplicateContext ) { PCRYPT_SERVER_CONTEXT pServerContext = (PCRYPT_SERVER_CONTEXT)pvDuplicateContext; if( pServerContext == NULL || pServerContext->cbSize != sizeof(CRYPT_SERVER_CONTEXT) ) { return ERROR_INVALID_PARAMETER; } if( pServerContext->hToken ) CloseHandle( pServerContext->hToken ); if(pServerContext->szUserStorageArea) { SSFree(pServerContext->szUserStorageArea); pServerContext->szUserStorageArea = NULL; } ZeroMemory( pServerContext, sizeof(CRYPT_SERVER_CONTEXT) ); SSFree( pServerContext ); return ERROR_SUCCESS; } DWORD WINAPI CPSImpersonateClient( IN PVOID pvContext ) { PCRYPT_SERVER_CONTEXT pServerContext = (PCRYPT_SERVER_CONTEXT)pvContext; DWORD dwLastError = ERROR_INVALID_PARAMETER; if( pvContext == NULL ) return ERROR_SUCCESS; if( pServerContext->cbSize == sizeof(CRYPT_SERVER_CONTEXT) ) { if( pServerContext->fOverrideToLocalSystem || (pServerContext->WellKnownAccount != 0)) { if(ImpersonateSelf(SecurityImpersonation)) { dwLastError = ERROR_SUCCESS; } else { dwLastError = GetLastError(); D_DebugLog((DEB_WARN, "Failed ImpersonateSelf call: 0x%x\n", dwLastError)); } } else { // // duplicated server context has access token included; use it directly // if( pServerContext->hToken ) { if(!SetThreadToken( NULL, pServerContext->hToken )) { dwLastError = GetLastError(); D_DebugLog((DEB_WARN, "Failed SetThreadToken call: 0x%x\n", dwLastError)); goto cleanup; } dwLastError = ERROR_SUCCESS; goto cleanup; } if(pServerContext->hBinding) { dwLastError = RpcImpersonateClient( pServerContext->hBinding ); } else { dwLastError = ERROR_INVALID_PARAMETER; } } } cleanup: #if DBG if(NT_SUCCESS(dwLastError) && (DPAPIInfoLevel & DEB_TRACE)) { BYTE rgbTemp[256]; PTOKEN_USER pUser = (PTOKEN_USER)rgbTemp; DWORD cbRetInfo; UNICODE_STRING ucsSid; HANDLE hToken; NTSTATUS Status; Status = NtOpenThreadToken(NtCurrentThread(), TOKEN_QUERY, TRUE, &hToken); if(NT_SUCCESS(Status)) { Status = NtQueryInformationToken(hToken, TokenUser, pUser, 256, &cbRetInfo); if(NT_SUCCESS(Status)) { if(NT_SUCCESS(RtlConvertSidToUnicodeString(&ucsSid, pUser->User.Sid, TRUE))) { D_DebugLog((DEB_TRACE, "Impersonating user:%ls\n", ucsSid.Buffer)); RtlFreeUnicodeString(&ucsSid); } } else { D_DebugLog((DEB_ERROR, "Unable read user info: 0x%x\n", Status)); } CloseHandle(hToken); } else { D_DebugLog((DEB_ERROR, "Unable to open thread token: 0x%x\n", Status)); } } #endif if(!NT_SUCCESS(dwLastError)) { D_DebugLog((DEB_WARN, "CPSImpersonateClient returned 0x%x\n", dwLastError)); } return dwLastError; } DWORD WINAPI CPSRevertToSelf( IN PVOID pvContext ) { PCRYPT_SERVER_CONTEXT pServerContext = (PCRYPT_SERVER_CONTEXT)pvContext; DWORD dwLastError = ERROR_INVALID_PARAMETER; if( pvContext == NULL ) return ERROR_SUCCESS; if( pServerContext->cbSize == sizeof(CRYPT_SERVER_CONTEXT) ) { if( pServerContext->fOverrideToLocalSystem || pServerContext->hToken ) { if(RevertToSelf()) { dwLastError = ERROR_SUCCESS; } else { dwLastError = GetLastError(); } } else { if(pServerContext->hBinding) { dwLastError = RpcRevertToSelfEx( pServerContext->hBinding ); } else { dwLastError = ERROR_INVALID_PARAMETER; } } } return dwLastError; } DWORD WINAPI CPSOverrideToLocalSystem( IN PVOID pvContext, IN BOOL *pfLocalSystem, // if non-null, new over ride BOOL IN OUT BOOL *pfCurrentlyLocalSystem // if non-null, prior over ride BOOL ) { PCRYPT_SERVER_CONTEXT pServerContext = (PCRYPT_SERVER_CONTEXT)pvContext; if( pServerContext == NULL || pServerContext->cbSize != sizeof(CRYPT_SERVER_CONTEXT) ) { return ERROR_INVALID_PARAMETER; } if( pfCurrentlyLocalSystem ) *pfCurrentlyLocalSystem = pServerContext->fOverrideToLocalSystem; if( pfLocalSystem ) pServerContext->fOverrideToLocalSystem = *pfLocalSystem; return ERROR_SUCCESS; } DWORD WINAPI CPSSetWellKnownAccount( IN PVOID pvContext, IN DWORD dwAccount) { PCRYPT_SERVER_CONTEXT pServerContext = (PCRYPT_SERVER_CONTEXT)pvContext; if( pServerContext == NULL || pServerContext->cbSize != sizeof(CRYPT_SERVER_CONTEXT)) { return ERROR_INVALID_PARAMETER; } if(dwAccount != pServerContext->WellKnownAccount) { // We're setting the context account to a new value, // so NULL out the cached path string. A new one will be // created automatically, as necessary. if(pServerContext->szUserStorageArea) { SSFree(pServerContext->szUserStorageArea); pServerContext->szUserStorageArea = NULL; } pServerContext->WellKnownAccount = dwAccount; } return ERROR_SUCCESS; } DWORD WINAPI CPSQueryWellKnownAccount( IN PVOID pvContext, OUT DWORD *pdwAccount) { PCRYPT_SERVER_CONTEXT pServerContext = (PCRYPT_SERVER_CONTEXT)pvContext; if( pServerContext == NULL || pServerContext->cbSize != sizeof(CRYPT_SERVER_CONTEXT)) { return ERROR_INVALID_PARAMETER; } *pdwAccount = pServerContext->WellKnownAccount; return ERROR_SUCCESS; } DWORD CPSDuplicateClientAccessToken( IN PVOID pvContext, // server context IN OUT HANDLE *phToken ) { HANDLE hToken = NULL; HANDLE hDuplicateToken; DWORD dwLastError = ERROR_SUCCESS; BOOL fSuccess; *phToken = NULL; // // make a duplicate of the client access token. // PCRYPT_SERVER_CONTEXT pServerContext = (PCRYPT_SERVER_CONTEXT)pvContext; if(!DuplicateTokenEx(pServerContext->hToken, 0, NULL, SecurityImpersonation, TokenImpersonation, &hDuplicateToken )) { dwLastError = GetLastError(); } else *phToken = hDuplicateToken; return dwLastError; } DWORD WINAPI CPSGetUserName( IN PVOID pvContext, OUT LPWSTR *ppszUserName, OUT DWORD *pcchUserName ) { WCHAR szBuf[MAX_PATH+1]; DWORD cchBuf = MAX_PATH; DWORD dwLastError = ERROR_INVALID_PARAMETER; BOOL fLocalMachine = FALSE; BOOL fSuccess = FALSE; DWORD dwAccount = 0; // // if we are currently over-riding to Local System, we know the values // that need to be returned. // CPSOverrideToLocalSystem( pvContext, NULL, // don't change current over-ride BOOL &fLocalMachine ); CPSQueryWellKnownAccount(pvContext, &dwAccount); if( fLocalMachine || dwAccount == DP_ACCOUNT_LOCAL_SYSTEM) { static const WCHAR szName1[] = TEXTUAL_SID_LOCAL_SYSTEM; CopyMemory( szBuf, szName1, sizeof(szName1) ); cchBuf = sizeof(szName1) / sizeof(WCHAR); fSuccess = TRUE; } else if(dwAccount == DP_ACCOUNT_LOCAL_SERVICE) { static const WCHAR szName2[] = TEXTUAL_SID_LOCAL_SERVICE; CopyMemory( szBuf, szName2, sizeof(szName2) ); cchBuf = sizeof(szName2) / sizeof(WCHAR); fSuccess = TRUE; } else if(dwAccount == DP_ACCOUNT_NETWORK_SERVICE) { static const WCHAR szName3[] = TEXTUAL_SID_NETWORK_SERVICE; CopyMemory( szBuf, szName3, sizeof(szName3) ); cchBuf = sizeof(szName3) / sizeof(WCHAR); fSuccess = TRUE; } else { dwLastError = CPSImpersonateClient( pvContext ); if(dwLastError != ERROR_SUCCESS) return dwLastError; fSuccess = GetUserTextualSid( NULL, szBuf, &cchBuf ); CPSRevertToSelf( pvContext ); } dwLastError = ERROR_NOT_ENOUGH_MEMORY; if( fSuccess ) { *ppszUserName = (LPWSTR)SSAlloc( cchBuf * sizeof(WCHAR)); if(*ppszUserName) { CopyMemory( *ppszUserName, szBuf, cchBuf * sizeof(WCHAR)); if(pcchUserName) *pcchUserName = cchBuf; dwLastError = ERROR_SUCCESS; } } return dwLastError; } DWORD WINAPI CPSGetDerivedCredential( IN PVOID pvContext, OUT GUID *pCredentialID, IN DWORD dwFlags, IN PBYTE pbMixingBytes, IN DWORD cbMixingBytes, IN OUT BYTE rgbDerivedCredential[A_SHA_DIGEST_LEN] ) { LUID LogonId; PCRYPT_SERVER_CONTEXT pServerContext = (PCRYPT_SERVER_CONTEXT)pvContext; DWORD dwLastError = ERROR_SUCCESS; if( pServerContext != NULL && pServerContext->cbSize != sizeof(CRYPT_SERVER_CONTEXT) ) return ERROR_INVALID_PARAMETER; if( cbMixingBytes == 0 || pbMixingBytes == NULL ) return ERROR_INVALID_PARAMETER; // // impersonate the client and get the LogonId associated with the client. // dwLastError = CPSImpersonateClient( pvContext ); if( dwLastError != ERROR_SUCCESS ) return dwLastError; if(!GetThreadAuthenticationId( GetCurrentThread(), &LogonId )) { dwLastError = GetLastError(); CPSRevertToSelf( pvContext ); // don't check error return since we're already failing return dwLastError; } dwLastError = QueryDerivedCredential( pCredentialID, &LogonId, dwFlags, pbMixingBytes, cbMixingBytes, rgbDerivedCredential ); CPSRevertToSelf( pvContext ); return dwLastError; } DWORD WINAPI CPSGetSystemCredential( IN PVOID pvContext, IN BOOL fLocalMachine, IN OUT BYTE rgbSystemCredential[A_SHA_DIGEST_LEN] ) { PCRYPT_SERVER_CONTEXT pServerContext = (PCRYPT_SERVER_CONTEXT)pvContext; if( pServerContext != NULL && pServerContext->cbSize != sizeof(CRYPT_SERVER_CONTEXT) ) return ERROR_INVALID_PARAMETER; return GetSystemCredential( fLocalMachine, rgbSystemCredential); } DWORD WINAPI CPSCreateWorkerThread( IN PVOID pThreadFunc, IN PVOID pThreadArg ) { if( !QueueUserWorkItem( (PTHREAD_START_ROUTINE)pThreadFunc, pThreadArg, WT_EXECUTELONGFUNCTION )) { return GetLastError(); } return ERROR_SUCCESS; } DWORD CPSAudit( IN HANDLE hToken, IN DWORD dwAuditID, IN LPCWSTR wszMasterKeyID, IN LPCWSTR wszRecoveryServer, IN DWORD dwReason, IN LPCWSTR wszRecoveryKeyID, IN DWORD dwFailure) { NTSTATUS Status = STATUS_SUCCESS; UNICODE_STRING MasterKeyID; UNICODE_STRING RecoveryServer; UNICODE_STRING RecoveryKeyID; BOOL fReasonField = FALSE; PSID UserSid = NULL; fReasonField = ((SE_AUDITID_DPAPI_PROTECT == dwAuditID) || (SE_AUDITID_DPAPI_UNPROTECT == dwAuditID) || (SE_AUDITID_DPAPI_RECOVERY == dwAuditID)); GetTokenUserSid(hToken, &UserSid); if(wszMasterKeyID) { RtlInitUnicodeString(&MasterKeyID, wszMasterKeyID); } else { RtlInitUnicodeString(&MasterKeyID, L""); } if(wszRecoveryServer) { RtlInitUnicodeString(&RecoveryServer, wszRecoveryServer); } else { RtlInitUnicodeString(&RecoveryServer, L""); } if(wszRecoveryKeyID) { RtlInitUnicodeString(&RecoveryKeyID, wszRecoveryKeyID); } else { RtlInitUnicodeString(&RecoveryKeyID, L""); } Status = LsaIAuditDPAPIEvent( dwAuditID, UserSid, &MasterKeyID, &RecoveryServer, fReasonField ? &dwReason : NULL, &RecoveryKeyID, &dwFailure ); if(UserSid) { SSFree(UserSid); } return (DWORD)Status; } DWORD CPSGetUserStorageArea( IN PVOID pvContext, IN PSID pSid, // optional IN BOOL fCreate, // Create the storage area if it doesn't exist IN OUT LPWSTR *ppszUserStorageArea ) { PCRYPT_SERVER_CONTEXT pServerContext = (PCRYPT_SERVER_CONTEXT)pvContext; WCHAR szUserStorageRoot[MAX_PATH+1]; DWORD cbUserStorageRoot; const WCHAR szProductString[] = L"\\Microsoft\\Protect\\"; DWORD cbProductString = sizeof(szProductString) - sizeof(WCHAR); const WCHAR szUserStorageForSystem[] = L"\\User"; LPWSTR pszUser = NULL; DWORD cbUser; DWORD cchUser; LPCWSTR szOptionalTrailing; DWORD cbOptionalTrailing = 0; BOOL fLocalMachine = FALSE; DWORD dwAccount = 0; HANDLE hFile = INVALID_HANDLE_VALUE; PBYTE pbCurrent; BOOL fImpersonated = FALSE; DWORD dwLastError = ERROR_CANTOPEN; *ppszUserStorageArea = NULL; if(NULL == pServerContext) { return ERROR_INVALID_PARAMETER; } if(NULL == pSid) { // // If this is the current users sid, check to see if we've already // calculated this string. if(NULL != pServerContext->szUserStorageArea) { *ppszUserStorageArea = (LPWSTR)SSAlloc((wcslen(pServerContext->szUserStorageArea)+1)*sizeof(WCHAR)); if(NULL == *ppszUserStorageArea) { return ERROR_NOT_ENOUGH_MEMORY; } wcscpy(*ppszUserStorageArea, pServerContext->szUserStorageArea); return ERROR_SUCCESS; } // // get the user name associated with the call. // Note: this is the textual Sid on NT, and the user name on Win95. // dwLastError = CPSGetUserName( pvContext, &pszUser, &cchUser ); if(dwLastError != ERROR_SUCCESS) goto cleanup; cbUser = (cchUser-1) * sizeof(WCHAR); } else { WCHAR wszTextualSid[MAX_PATH+1]; cchUser = MAX_PATH; // Note that the number of characters returned from // GetTextualSid includes the zero-terminator. if(!GetTextualSid(pSid, wszTextualSid, &cchUser)) { dwLastError = ERROR_INVALID_PARAMETER; goto cleanup; } cbUser = (cchUser-1) * sizeof(WCHAR); pszUser = (LPWSTR)SSAlloc(cchUser*sizeof(WCHAR)); if(NULL == pszUser) { dwLastError = ERROR_NOT_ENOUGH_MEMORY; goto cleanup; } wcscpy(pszUser, wszTextualSid); } // // impersonate the client user to test and create storage area if necessary // dwLastError = CPSImpersonateClient( pvContext ); if( dwLastError != ERROR_SUCCESS ) goto cleanup; fImpersonated = TRUE; // // see if the call is for shared, CRYPT_PROTECT_LOCAL_MACHINE // disposition. // CPSOverrideToLocalSystem( pvContext, NULL, // don't change current over-ride BOOL &fLocalMachine ); CPSQueryWellKnownAccount( pvContext, &dwAccount); // // determine path to per-user storage area, based on whether this // is a local machine disposition call or a per-user disposition call. // if(fLocalMachine || (dwAccount != 0)) { cbUserStorageRoot = GetSystemDirectoryW( szUserStorageRoot, sizeof(szUserStorageRoot) / sizeof(WCHAR) ); cbUserStorageRoot *= sizeof(WCHAR); // // when the Sid is the SYSTEM sid, and this isn't a Local Machine // disposition call, add a trailing component to the storage path. // if((dwAccount == DP_ACCOUNT_LOCAL_SYSTEM) && !fLocalMachine) { cbOptionalTrailing = sizeof(szUserStorageForSystem) - sizeof(WCHAR); szOptionalTrailing = szUserStorageForSystem; } } else { dwLastError = PRGetProfilePath(NULL, szUserStorageRoot ); if( dwLastError != ERROR_SUCCESS ) { goto cleanup; } cbUserStorageRoot = lstrlenW( szUserStorageRoot ) * sizeof(WCHAR); } // // an empty string is not legal as the root component of the per-user // storage area. // if( cbUserStorageRoot == 0 ) { dwLastError = ERROR_CANTOPEN; goto cleanup; } // // ensure returned string does not have trailing \ // if( szUserStorageRoot[ (cbUserStorageRoot / sizeof(WCHAR)) - 1 ] == L'\\' ) { szUserStorageRoot[ (cbUserStorageRoot / sizeof(WCHAR)) - 1 ] = L'\0'; cbUserStorageRoot -= sizeof(WCHAR); } *ppszUserStorageArea = (LPWSTR)SSAlloc( cbUserStorageRoot + cbProductString + cbUser + cbOptionalTrailing + (2 * sizeof(WCHAR)) // trailing slash and NULL ); if( *ppszUserStorageArea == NULL ) { dwLastError = ERROR_NOT_ENOUGH_SERVER_MEMORY; goto cleanup; } pbCurrent = (PBYTE)*ppszUserStorageArea; CopyMemory(pbCurrent, szUserStorageRoot, cbUserStorageRoot); pbCurrent += cbUserStorageRoot; CopyMemory(pbCurrent, szProductString, cbProductString); pbCurrent += cbProductString; CopyMemory(pbCurrent, pszUser, cbUser); pbCurrent += cbUser; // note: cbUser does not include terminal NULL if(cbOptionalTrailing) { CopyMemory(pbCurrent, szOptionalTrailing, cbOptionalTrailing); pbCurrent += cbOptionalTrailing; } if( *((LPWSTR)pbCurrent - 1) != L'\\' ) { *(LPWSTR)pbCurrent = L'\\'; pbCurrent += sizeof(WCHAR); } *(LPWSTR)pbCurrent = L'\0'; // // test for well-known file in the storage area. if it exists, // don't bother trying to create directory structure. // dwLastError = OpenFileInStorageArea( NULL, // NULL == already impersonating the client GENERIC_READ, *ppszUserStorageArea, REGVAL_PREFERRED_MK, &hFile ); if( dwLastError == ERROR_SUCCESS) { CloseHandle( hFile ); } else { if(fCreate) { dwLastError = DPAPICreateNestedDirectories( *ppszUserStorageArea, (LPWSTR)((LPBYTE)*ppszUserStorageArea + cbUserStorageRoot + sizeof(WCHAR)) ); } } if((ERROR_SUCCESS == dwLastError) && (NULL == pSid)) { pServerContext->szUserStorageArea = (LPWSTR)SSAlloc((wcslen(*ppszUserStorageArea)+1)*sizeof(WCHAR)); if(NULL == pServerContext->szUserStorageArea) { dwLastError = ERROR_NOT_ENOUGH_MEMORY; } else { wcscpy(pServerContext->szUserStorageArea, *ppszUserStorageArea); } } cleanup: if( fImpersonated ) CPSRevertToSelf( pvContext ); if(pszUser) SSFree(pszUser); if( dwLastError != ERROR_SUCCESS && *ppszUserStorageArea ) { SSFree( *ppszUserStorageArea ); *ppszUserStorageArea = NULL; } return dwLastError; } #if 0 HKEY GetLMRegistryProviderKey() { HKEY hBaseKey = NULL; DWORD dwCreate; DWORD dwDesiredAccess = KEY_READ | KEY_WRITE; static const WCHAR szKeyName[] = REG_CRYPTPROTECT_LOC L"\\" REG_CRYPTPROTECT_PROVIDERS_SUBKEYLOC; // Open Key // if (ERROR_SUCCESS != RegCreateKeyExU( HKEY_LOCAL_MACHINE, szKeyName, 0, NULL, // address of class string 0, dwDesiredAccess, NULL, &hBaseKey, &dwCreate)) goto Ret; Ret: return hBaseKey; } #endif DWORD GetPolicyBits() { return 0; } /////////////////////////////////////////////////////////////////////// // RPC-exposed functions // // these functions return a DWORD equivalent to GetLastError(). // the client side stub code will check if the return code is not // ERROR_SUCCESS, and if this is the case, the client stub will return // FALSE and SetLastError() to this DWORD. // DWORD s_SSCryptProtectData( handle_t h, BYTE __RPC_FAR *__RPC_FAR *ppbOut, DWORD __RPC_FAR *pcbOut, BYTE __RPC_FAR *pbIn, DWORD cbIn, LPCWSTR szDataDescr, BYTE* pbOptionalEntropy, DWORD cbOptionalEntropy, PSSCRYPTPROTECTDATA_PROMPTSTRUCT pPromptStruct, DWORD dwFlags, BYTE* pbOptionalPassword, DWORD cbOptionalPassword ) { DWORD dwRet; DWORD dwHeaderSize = sizeof(GUID)+sizeof(DWORD); PBYTE pbWritePtr; PBYTE pTemp; CRYPT_SERVER_CONTEXT ServerContext; // User mode cannot request encryption of system blobs if(dwFlags & CRYPTPROTECT_SYSTEM) { return ERROR_INVALID_PARAMETER; } // Create a server context. dwRet = CPSCreateServerContext(&ServerContext, h); if(dwRet != ERROR_SUCCESS) { return dwRet; } // get policy for this level // UNDONE: what do policy bits allow an admin to set? // maybe a recovery agent, other defaults? GetPolicyBits(); dwRet = SPCryptProtect( &ServerContext, ppbOut, pcbOut, pbIn, cbIn, szDataDescr, pbOptionalEntropy, cbOptionalEntropy, pPromptStruct, dwFlags, pbOptionalPassword, // following 2 fields considered temporary cbOptionalPassword // until SAS UI supported ); RtlSecureZeroMemory( pbIn, cbIn ); if ( dwRet != ERROR_SUCCESS || *ppbOut == NULL ) goto Ret; // move entire block down, sneak header in pTemp = (PBYTE)SSReAlloc(*ppbOut, *pcbOut + dwHeaderSize); if(NULL == pTemp) { dwRet = ERROR_NOT_ENOUGH_MEMORY; SSFree(*ppbOut); *ppbOut = NULL; *pcbOut = 0; goto Ret; } *ppbOut = pTemp; MoveMemory(*ppbOut + dwHeaderSize, *ppbOut, *pcbOut); *pcbOut += dwHeaderSize; pbWritePtr = *ppbOut; *(DWORD*)pbWritePtr = CRYPTPROTECT_SVR_VERSION_1; pbWritePtr += sizeof(DWORD); CopyMemory(pbWritePtr, &g_guidDefaultProvider, sizeof(GUID)); pbWritePtr += sizeof(GUID); dwRet = ERROR_SUCCESS; Ret: CPSDeleteServerContext( &ServerContext ); return dwRet; } DWORD s_SSCryptUnprotectData( handle_t h, BYTE __RPC_FAR *__RPC_FAR *ppbOut, DWORD __RPC_FAR *pcbOut, BYTE __RPC_FAR *pbIn, DWORD cbIn, LPWSTR* ppszDataDescr, BYTE* pbOptionalEntropy, DWORD cbOptionalEntropy, PSSCRYPTPROTECTDATA_PROMPTSTRUCT pPromptStruct, DWORD dwFlags, BYTE* pbOptionalPassword, DWORD cbOptionalPassword ) { DWORD dwRet; PBYTE pbReadPtr = pbIn; GUID guidProvider; CRYPT_SERVER_CONTEXT ServerContext; // Zero output parameters. *ppbOut = NULL; *pcbOut = 0; // User mode cannot request decryption of system blobs if(dwFlags & CRYPTPROTECT_SYSTEM) { return ERROR_INVALID_PARAMETER; } // Error out if input buffer is smaller than the minumum size. if(cbIn < sizeof(DWORD) + sizeof(GUID)) { return ERROR_INVALID_DATA; } // Create a server context. dwRet = CPSCreateServerContext(&ServerContext, h); if(dwRet != ERROR_SUCCESS) { return dwRet; } // UNDONE: what do policy bits allow an admin to set? // maybe a recovery agent, other defaults? GetPolicyBits(); if (*(DWORD*)pbReadPtr != CRYPTPROTECT_SVR_VERSION_1) { dwRet = ERROR_INVALID_DATA; goto Ret; } pbReadPtr += sizeof(DWORD); // next field in Data is provider GUID CopyMemory(&guidProvider, pbReadPtr, sizeof(GUID)); pbReadPtr += sizeof(GUID); dwRet = SPCryptUnprotect( &ServerContext, ppbOut, pcbOut, pbReadPtr, (cbIn - (LONG)(pbReadPtr - pbIn)) , // eg (200 - (0x00340020 - 0x00340000)) ppszDataDescr, pbOptionalEntropy, cbOptionalEntropy, pPromptStruct, dwFlags, pbOptionalPassword, // following 2 fields considered temporary cbOptionalPassword // until SAS UI supported ); RtlSecureZeroMemory( pbIn, cbIn ); if (dwRet != ERROR_SUCCESS) goto Ret; dwRet = ERROR_SUCCESS; Ret: CPSDeleteServerContext( &ServerContext ); return dwRet; } BOOLEAN LsaICryptProtectData( IN PVOID DataIn, IN ULONG DataInLength, IN PUNICODE_STRING DataDescr, IN PVOID OptionalEntropy, IN ULONG OptionalEntropyLength, IN PVOID Reserved, IN PVOID Reserved2, IN ULONG Flags, OUT PVOID * DataOut, OUT PULONG DataOutLength) { DWORD dwRetVal = ERROR_SUCCESS; CRYPT_SERVER_CONTEXT ServerContext;; DWORD dwHeaderSize = sizeof(GUID)+sizeof(DWORD); PBYTE pbWritePtr; PBYTE pTemp; dwRetVal = CPSCreateServerContext(&ServerContext, NULL); if(dwRetVal != ERROR_SUCCESS) { SetLastError(dwRetVal); return FALSE; } // check params if ((DataOut == NULL) || (DataIn == NULL) || (NULL == DataDescr)) { dwRetVal = ERROR_INVALID_PARAMETER; goto error; } if(ServerContext.fImpersonating) { CPSRevertToSelf(&ServerContext); } // get policy for this level // UNDONE: what do policy bits allow an admin to set? // maybe a recovery agent, other defaults? GetPolicyBits(); *DataOut = NULL; *DataOutLength = 0; dwRetVal = SPCryptProtect( &ServerContext, (PBYTE *)DataOut, DataOutLength, (PBYTE)DataIn, DataInLength, DataDescr?DataDescr->Buffer:NULL, (PBYTE)OptionalEntropy, OptionalEntropyLength, NULL, Flags, NULL, // following 2 fields considered temporary 0 // until SAS UI supported ); if ( dwRetVal != ERROR_SUCCESS || *DataOut == NULL ) goto error; // move entire block down, sneak header in pTemp =(PBYTE) SSReAlloc(*DataOut, *DataOutLength + dwHeaderSize); if(NULL == pTemp) { dwRetVal = ERROR_NOT_ENOUGH_MEMORY; SSFree(*DataOut); *DataOut = NULL; *DataOutLength = 0; goto error; } *DataOut = pTemp; MoveMemory((PBYTE)*DataOut + dwHeaderSize, *DataOut, *DataOutLength); *DataOutLength += dwHeaderSize; pbWritePtr = (PBYTE)*DataOut; *(DWORD*)pbWritePtr = CRYPTPROTECT_SVR_VERSION_1; pbWritePtr += sizeof(DWORD); CopyMemory(pbWritePtr, &g_guidDefaultProvider, sizeof(GUID)); pbWritePtr += sizeof(GUID); error: if(ServerContext.fImpersonating) { CPSImpersonateClient(&ServerContext); } CPSDeleteServerContext( &ServerContext ); if(dwRetVal != ERROR_SUCCESS) { SetLastError(dwRetVal); return FALSE; } return TRUE; } BOOLEAN LsaICryptUnprotectData( IN PVOID DataIn, IN ULONG DataInLength, IN PVOID OptionalEntropy, IN ULONG OptionalEntropyLength, IN PVOID Reserved, IN PVOID Reserved2, IN ULONG Flags, OUT PUNICODE_STRING DataDescr, OUT PVOID * DataOut, OUT PULONG DataOutLength) { DWORD dwRetVal = ERROR_SUCCESS; CRYPT_SERVER_CONTEXT ServerContext;; DWORD dwHeaderSize = sizeof(GUID)+sizeof(DWORD); PBYTE pbWritePtr; LPWSTR wszDataDescr = NULL; dwRetVal = CPSCreateServerContext(&ServerContext, NULL); if(dwRetVal != ERROR_SUCCESS) { SetLastError(dwRetVal); return FALSE; } // check params if ((DataOut == NULL) || (DataIn == NULL)) { dwRetVal = ERROR_INVALID_PARAMETER; goto error; } if(ServerContext.fImpersonating) { CPSRevertToSelf(&ServerContext); } // don't validate flags parameter // get policy for this level // UNDONE: what do policy bits allow an admin to set? // maybe a recovery agent, other defaults? GetPolicyBits(); // // define outer+inner wrapper for security blob. // this won't be necessary once SAS support is provided by the OS. // typedef struct { DWORD dwOuterVersion; GUID guidProvider; DWORD dwVersion; GUID guidMK; DWORD dwPromptFlags; DWORD cbDataDescr; WCHAR szDataDescr[1]; } sec_blob, *psec_blob; sec_blob *SecurityBlob = (sec_blob*)(DataIn); // // zero so client stub allocates // *DataOut = NULL; *DataOutLength = 0; // // only call UI function if prompt flags dictate, because we don't // want to bring in cryptui.dll unless necessary. // if( ((SecurityBlob->dwPromptFlags & CRYPTPROTECT_PROMPT_ON_UNPROTECT) || (SecurityBlob->dwPromptFlags & CRYPTPROTECT_PROMPT_ON_PROTECT)) ) { dwRetVal = ERROR_INVALID_PARAMETER; goto error; } else { if(SecurityBlob->dwPromptFlags & CRYPTPROTECT_PROMPT_STRONG ) { dwRetVal = ERROR_INVALID_PARAMETER; goto error; } } if (SecurityBlob->dwOuterVersion != CRYPTPROTECT_SVR_VERSION_1) { dwRetVal = ERROR_INVALID_DATA; goto error; } if(0 != memcmp(&SecurityBlob->guidProvider, &g_guidDefaultProvider, sizeof(GUID))) { dwRetVal = ERROR_INVALID_DATA; goto error; } Flags |= CRYPTPROTECT_IN_PROCESS; dwRetVal = SPCryptUnprotect( &ServerContext, (PBYTE *)DataOut, DataOutLength, (PBYTE)DataIn + sizeof(DWORD) + sizeof(GUID), (DataInLength - (LONG)(sizeof(DWORD) + sizeof(GUID))) , DataDescr?&wszDataDescr:NULL, (PBYTE)OptionalEntropy, OptionalEntropyLength, NULL, Flags, NULL, // following 2 fields considered temporary 0 // until SAS UI supported ); if (dwRetVal != ERROR_SUCCESS) { goto error; } if(NULL != DataDescr) { RtlInitUnicodeString(DataDescr, wszDataDescr); } error: if(ServerContext.fImpersonating) { // Impersonate back to the impersonation context CPSImpersonateClient(&ServerContext); } CPSDeleteServerContext( &ServerContext ); SetLastError(dwRetVal); if(dwRetVal != ERROR_SUCCESS && dwRetVal != CRYPT_I_NEW_PROTECTION_REQUIRED ) { return FALSE; } return TRUE; } DWORD WINAPI CPSGetSidHistory( IN PVOID pvContext, OUT PSID **papsidHistory, OUT DWORD *cpsidHistory ) { DWORD dwLastError = ERROR_SUCCESS; BYTE FastBuffer[256]; BYTE GroupsFastBuffer[256]; LPBYTE SlowBuffer = NULL; PTOKEN_USER ptgUser; PTOKEN_GROUPS ptgGroups = NULL; DWORD cbBuffer; DWORD cbSid; DWORD cSids = 0; PBYTE pbCurrentSid = NULL; DWORD i; PCRYPT_SERVER_CONTEXT pServerContext = (PCRYPT_SERVER_CONTEXT)pvContext; // // try querying based on a fast stack based buffer first. // ptgUser = (PTOKEN_USER)FastBuffer; cbBuffer = sizeof(FastBuffer); if(!GetTokenInformation( pServerContext->hToken, // identifies access token TokenUser, // TokenUser info type ptgUser, // retrieved info buffer cbBuffer, // size of buffer passed-in &cbBuffer // required buffer size )) { dwLastError = GetLastError(); if(dwLastError != ERROR_INSUFFICIENT_BUFFER) { goto error; } // // try again with the specified buffer size // ptgUser = (PTOKEN_USER)SSAlloc(cbBuffer); if(NULL == ptgUser) { dwLastError = ERROR_NOT_ENOUGH_MEMORY; goto error; } if(!GetTokenInformation( pServerContext->hToken, // identifies access token TokenUser, // TokenUser info type ptgUser, // retrieved info buffer cbBuffer, // size of buffer passed-in &cbBuffer // required buffer size )) { dwLastError = GetLastError(); goto error; } } // // try querying based on a fast stack based buffer first. // ptgGroups = (PTOKEN_GROUPS)GroupsFastBuffer; cbBuffer = sizeof(GroupsFastBuffer); if(!GetTokenInformation( pServerContext->hToken, // identifies access token TokenGroups, // TokenUser info type ptgGroups, // retrieved info buffer cbBuffer, // size of buffer passed-in &cbBuffer // required buffer size )) { dwLastError = GetLastError(); if(dwLastError != ERROR_INSUFFICIENT_BUFFER) { goto error; } dwLastError = ERROR_SUCCESS; // // try again with the specified buffer size // ptgGroups = (PTOKEN_GROUPS)SSAlloc(cbBuffer); if(NULL == ptgGroups) { dwLastError = ERROR_NOT_ENOUGH_MEMORY; goto error; } if(!GetTokenInformation( pServerContext->hToken, // identifies access token TokenGroups, // TokenUser info type ptgGroups, // retrieved info buffer cbBuffer, // size of buffer passed-in &cbBuffer // required buffer size )) { dwLastError = GetLastError(); goto error; } } // // if we got the token info successfully, copy the // relevant element for the caller. // cbSid = GetLengthSid(ptgUser->User.Sid); cSids = 1; for(i=0; i < ptgGroups->GroupCount; i++) { if(0 == (SE_GROUP_ENABLED & ptgGroups->Groups[i].Attributes)) { continue; } if(0 == ((SE_GROUP_OWNER | SE_GROUP_USE_FOR_DENY_ONLY) & ptgGroups->Groups[i].Attributes)) { continue; } cbSid += GetLengthSid(ptgGroups->Groups[i].Sid); cSids ++; } *cpsidHistory = cSids; *papsidHistory = (PSID *)SSAlloc(cSids*sizeof(PSID) + cbSid ); if(*papsidHistory == NULL) { dwLastError = ERROR_NOT_ENOUGH_MEMORY; goto error; } else { pbCurrentSid = (PBYTE)((*papsidHistory)+cSids); // Fill in the primary user SID (*papsidHistory)[0] = (PSID)pbCurrentSid; cbSid = GetLengthSid(ptgUser->User.Sid); CopySid(cbSid, pbCurrentSid, ptgUser->User.Sid); pbCurrentSid += cbSid; cSids = 1; // Fill in the rest of the SIDs for(i=0; i < ptgGroups->GroupCount; i++) { if(0 == (SE_GROUP_ENABLED & ptgGroups->Groups[i].Attributes)) { continue; } if(0 == ((SE_GROUP_OWNER | SE_GROUP_USE_FOR_DENY_ONLY) & ptgGroups->Groups[i].Attributes)) { continue; } (*papsidHistory)[cSids++] = pbCurrentSid; cbSid = GetLengthSid(ptgGroups->Groups[i].Sid); CopySid(cbSid, pbCurrentSid,ptgGroups->Groups[i].Sid); pbCurrentSid += cbSid; } } error: if(FastBuffer != (PBYTE)ptgUser) { SSFree(ptgUser); } if(GroupsFastBuffer != (PBYTE)ptgGroups) { SSFree(ptgGroups); } return dwLastError; }