/********************************************************************/ /** Copyright(c) 1995 Microsoft Corporation. **/ /********************************************************************/ //*** // // Filename: creds.c // // Description: Routines for storing and retrieving user Lsa secret // dial parameters. // // // History: 11/02/95 Anthony Discolo created // May 11,1995 NarenG modified for router. // #include #include #include #include #include #include #include #include #include #include #include #define MPR_CREDENTIALS_KEY TEXT("MprCredentials%d") #define MAX_REGISTRY_VALUE_LENGTH ((64*1024) - 1) typedef struct _MPRPARAMSENTRY { LIST_ENTRY ListEntry; WCHAR szPhoneBookEntryName[MAX_INTERFACE_NAME_LEN + 1]; WCHAR szUserName[UNLEN + 1]; WCHAR szPassword[PWLEN + 1]; WCHAR szDomain[DNLEN + 1]; } MPRPARAMSENTRY, *PMPRPARAMSENTRY; //** // // Call: ReadDialParamsBlob // // Returns: NO_ERROR - Success // Non-zero returns - Failure // // Description: // DWORD ReadDialParamsBlob( IN LPWSTR lpwsServer, OUT PVOID *ppvData, OUT LPDWORD lpdwSize ) { NTSTATUS status; DWORD dwRetCode = NO_ERROR; DWORD dwIndex; DWORD dwSize = 0; PVOID pvData = NULL; PVOID pvNewData = NULL; UNICODE_STRING unicodeKey; UNICODE_STRING unicodeServer; PUNICODE_STRING punicodeValue = NULL; OBJECT_ATTRIBUTES objectAttributes; LSA_HANDLE hPolicy; WCHAR wchKey[sizeof(MPR_CREDENTIALS_KEY) + 10 ]; // // Initialize return value. // *ppvData = NULL; *lpdwSize = 0; // // Open the LSA secret space for reading. // InitializeObjectAttributes( &objectAttributes, NULL, 0L, NULL, NULL ); RtlInitUnicodeString( &unicodeServer, lpwsServer ); status = LsaOpenPolicy( &unicodeServer, &objectAttributes, POLICY_READ, &hPolicy ); if ( status != STATUS_SUCCESS ) { return( LsaNtStatusToWinError( status ) ); } for( dwIndex = 0; TRUE; dwIndex++ ) { // // Format the key string. // wsprintf( wchKey, MPR_CREDENTIALS_KEY, dwIndex ); RtlInitUnicodeString( &unicodeKey, wchKey ); // // Get the value. // status = LsaRetrievePrivateData( hPolicy, &unicodeKey, &punicodeValue ); if ( status != STATUS_SUCCESS ) { dwRetCode = LsaNtStatusToWinError( status ); if ( dwRetCode == ERROR_FILE_NOT_FOUND ) { dwRetCode = NO_ERROR; } break; } if ( punicodeValue == NULL ) { dwRetCode = ERROR_NOT_ENOUGH_MEMORY; break; } // // Concatenate the strings. // pvNewData = LocalAlloc( LPTR, dwSize + punicodeValue->Length ); if ( pvNewData == NULL ) { dwRetCode = ERROR_NOT_ENOUGH_MEMORY; break; } if ( pvData != NULL ) { RtlCopyMemory( pvNewData, pvData, dwSize ); ZeroMemory( pvData, dwSize ); LocalFree( pvData ); pvData = NULL; } RtlCopyMemory( (PBYTE)pvNewData + dwSize, punicodeValue->Buffer, punicodeValue->Length ); pvData = pvNewData; dwSize += punicodeValue->Length; SecureZeroMemory(punicodeValue->Buffer, punicodeValue->Length); LsaFreeMemory( punicodeValue ); punicodeValue = NULL; } LsaClose( hPolicy ); if ( dwRetCode != NO_ERROR ) { if ( pvData != NULL ) { ZeroMemory( pvData, dwSize ); LocalFree( pvData ); } pvData = NULL; dwSize = 0; } if ( punicodeValue != NULL ) { LsaFreeMemory( punicodeValue ); } *ppvData = pvData; *lpdwSize = dwSize; return( dwRetCode ); } //** // // Call: WriteDialParamsBlob // // Returns: NO_ERROR - Success // Non-zero returns - Failure // // Description: // DWORD WriteDialParamsBlob( IN LPWSTR lpwsServer, IN PVOID pvData, IN DWORD dwcbData ) { NTSTATUS status; DWORD dwIndex = 0; DWORD dwRetCode = NO_ERROR; DWORD dwcb = 0; UNICODE_STRING unicodeKey, unicodeValue; UNICODE_STRING unicodeServer; OBJECT_ATTRIBUTES objectAttributes; LSA_HANDLE hPolicy; WCHAR wchKey[sizeof(MPR_CREDENTIALS_KEY) + 10 ]; // // Open the LSA secret space for writing. // InitializeObjectAttributes( &objectAttributes, NULL, 0L, NULL, NULL ); RtlInitUnicodeString( &unicodeServer, lpwsServer ); status = LsaOpenPolicy( &unicodeServer, &objectAttributes, POLICY_WRITE, &hPolicy); if (status != STATUS_SUCCESS) { return LsaNtStatusToWinError(status); } while( dwcbData ) { // // Format the key string. // wsprintf( wchKey, MPR_CREDENTIALS_KEY, dwIndex++ ); RtlInitUnicodeString( &unicodeKey, wchKey ); // // Write some of the key. // dwcb = ( dwcbData > MAX_REGISTRY_VALUE_LENGTH ) ? MAX_REGISTRY_VALUE_LENGTH : dwcbData; unicodeValue.Length = unicodeValue.MaximumLength = (USHORT)dwcb; unicodeValue.Buffer = pvData; status = LsaStorePrivateData( hPolicy, &unicodeKey, &unicodeValue ); if ( status != STATUS_SUCCESS ) { dwRetCode = LsaNtStatusToWinError(status); break; } // // Move the pointer to the unwritten part // of the value. // pvData = (PBYTE)pvData + dwcb; dwcbData -= dwcb; } if ( dwRetCode != NO_ERROR ) { LsaClose( hPolicy ); return( dwRetCode ); } // // Delete any extra keys. // for (;;) { // // Format the key string. // wsprintf( wchKey, MPR_CREDENTIALS_KEY, dwIndex++ ); RtlInitUnicodeString( &unicodeKey, wchKey ); // // Delete the key. // status = LsaStorePrivateData( hPolicy, &unicodeKey, NULL ); if ( status != STATUS_SUCCESS ) { break; } } LsaClose( hPolicy ); return( NO_ERROR ); } //** // // Call: DialParamsBlobToList // // Returns: NO_ERROR - Success // Non-zero returns - Failure // // Description: Take a string read from the user's registry key and produce // a list of MPRPARAMSENTRY structures. If one of the structures // has the same dwUID field as the dwUID passed in, then this // function returns a pointer to this structure. // // This string encodes the data for multiple MPRPARAMSENTRY // structures. The format of an encoded MPRPARAMSENTRY is as // follows: // // \0\0\0\0 // PMPRPARAMSENTRY DialParamsBlobToList( IN PVOID pvData, IN LPWSTR lpwsPhoneBookEntryName, OUT PLIST_ENTRY pHead ) { PWCHAR p; PMPRPARAMSENTRY pParams, pFoundParams; p = (PWCHAR)pvData; pFoundParams = NULL; for (;;) { pParams = LocalAlloc(LPTR, sizeof (MPRPARAMSENTRY)); if ( pParams == NULL ) { break; } wcscpy( pParams->szPhoneBookEntryName, p ); if (_wcsicmp(pParams->szPhoneBookEntryName,lpwsPhoneBookEntryName) == 0) { pFoundParams = pParams; } while (*p) p++; p++; wcscpy(pParams->szUserName, p); while (*p) p++; p++; wcscpy(pParams->szPassword, p); while (*p) p++; p++; wcscpy(pParams->szDomain, p); while (*p) p++; p++; InsertTailList(pHead, &pParams->ListEntry); if (*p == L'\0') break; } return( pFoundParams ); } //** // // Call: DialParamsListToBlob // // Returns: NO_ERROR - Success // Non-zero returns - Failure // // Description: // PVOID DialParamsListToBlob( IN PLIST_ENTRY pHead, OUT LPDWORD lpcb ) { DWORD dwcb, dwSize; PVOID pvData; PWCHAR p; PLIST_ENTRY pEntry; PMPRPARAMSENTRY pParams; // // Estimate a buffer size large enough to hold the new entry. // dwSize = *lpcb + sizeof (MPRPARAMSENTRY) + 32; if ( ( pvData = LocalAlloc(LPTR, dwSize) ) == NULL ) { return( NULL ); } // // Enumerate the list and convert each entry back to a string. // dwSize = 0; p = (PWCHAR)pvData; for (pEntry = pHead->Flink; pEntry != pHead; pEntry = pEntry->Flink ) { pParams = CONTAINING_RECORD(pEntry, MPRPARAMSENTRY, ListEntry); wcscpy(p, pParams->szPhoneBookEntryName); dwcb = wcslen(pParams->szPhoneBookEntryName) + 1; p += dwcb; dwSize += dwcb; wcscpy(p, pParams->szUserName); dwcb = wcslen(pParams->szUserName) + 1; p += dwcb; dwSize += dwcb; wcscpy(p, pParams->szPassword); dwcb = wcslen(pParams->szPassword) + 1; p += dwcb; dwSize += dwcb; wcscpy(p, pParams->szDomain); dwcb = wcslen(pParams->szDomain) + 1; p += dwcb; dwSize += dwcb; } *p = L'\0'; dwSize++; dwSize *= sizeof (WCHAR); // // Set the exact length here. // *lpcb = dwSize; return( pvData ); } //** // // Call: FreeParamsList // // Returns: NO_ERROR - Success // Non-zero returns - Failure // // Description: Will free the list of structures. // VOID FreeParamsList( IN PLIST_ENTRY pHead ) { PLIST_ENTRY pEntry; PMPRPARAMSENTRY pParams; while( !IsListEmpty(pHead) ) { pEntry = RemoveHeadList(pHead); pParams = CONTAINING_RECORD(pEntry, MPRPARAMSENTRY, ListEntry); SecureZeroMemory( pParams, sizeof( MPRPARAMSENTRY ) ); LocalFree(pParams); } } //** // // Call: RemoveCredentials // // Returns: NO_ERROR - Success // Non-zero returns - Failure // // Description: // DWORD RemoveCredentials( IN LPWSTR lpwsInterfaceName, IN LPVOID pvData, IN OUT LPDWORD lpdwSize ) { PWCHAR pWalker = (PWCHAR)pvData; DWORD dwIndex; // // There is no list to remove from. // if ( pvData == NULL ) { return( ERROR_NO_SUCH_INTERFACE ); } for (;;) { // // If we found the interface we want to remove, we jump past it // if ( _wcsicmp( (LPWSTR)pWalker, lpwsInterfaceName ) == 0 ) { PWCHAR pInterface = pWalker; DWORD dwEntrySize = 0; // // Jump past the 4 fields. // for ( dwIndex = 0; dwIndex < 4; dwIndex++ ) { while (*pWalker) pWalker++, dwEntrySize++; pWalker++, dwEntrySize++; } // // If this was the last entry in the list // if (*pWalker == L'\0') { ZeroMemory( pInterface, dwEntrySize ); } else { CopyMemory( pInterface, pWalker, *lpdwSize - ( (PBYTE)pWalker - (PBYTE)pvData ) ); } *lpdwSize -= dwEntrySize; return( NO_ERROR ); } else { // // Not found so skip over this entry // for ( dwIndex = 0; dwIndex < 4; dwIndex++ ) { while (*pWalker) pWalker++; pWalker++; } } if (*pWalker == L'\0') break; } return( ERROR_NO_SUCH_INTERFACE ); } //** // // Call: MprAdminInterfaceSetCredentials // // Returns: NO_ERROR - success // ERROR_INVALID_PARAMETER // // Description: // DWORD APIENTRY MprAdminInterfaceSetCredentialsInternal( IN LPWSTR lpwsServer OPTIONAL, IN LPWSTR lpwsInterfaceName, IN LPWSTR lpwsUserName OPTIONAL, IN LPWSTR lpwsDomainName OPTIONAL, IN LPWSTR lpwsPassword OPTIONAL ) { DWORD dwRetCode; DWORD dwSize; PVOID pvData; LIST_ENTRY paramList; PMPRPARAMSENTRY pParams = NULL; if ( lpwsInterfaceName == NULL ) { return( ERROR_INVALID_PARAMETER ); } if ( wcslen( lpwsInterfaceName ) > MAX_INTERFACE_NAME_LEN ) { return( ERROR_INVALID_PARAMETER ); } if ( lpwsUserName != NULL ) { if ( wcslen( lpwsUserName ) > UNLEN ) { return( ERROR_INVALID_PARAMETER ); } } if ( lpwsPassword != NULL ) { if ( wcslen( lpwsPassword ) > PWLEN ) { return( ERROR_INVALID_PARAMETER ); } } if ( lpwsDomainName != NULL ) { if ( wcslen( lpwsDomainName ) > DNLEN ) { return( ERROR_INVALID_PARAMETER ); } } // // Read the existing dial params string from the registry. // dwRetCode = ReadDialParamsBlob( lpwsServer, &pvData, &dwSize ); if ( dwRetCode != NO_ERROR ) { return( dwRetCode ); } // // If everything was NULL, we want to delete credentials for this interface // if ( ( lpwsUserName == NULL ) && ( lpwsDomainName == NULL ) && ( lpwsPassword == NULL ) ) { dwRetCode = RemoveCredentials( lpwsInterfaceName, pvData, &dwSize ); if ( dwRetCode != NO_ERROR ) { ZeroMemory ( pvData, dwSize ); LocalFree( pvData ); return( dwRetCode ); } } else { // // Parse the string into a list, and search for the phonebook entry. // InitializeListHead( ¶mList ); if ( pvData != NULL ) { pParams = DialParamsBlobToList(pvData,lpwsInterfaceName,¶mList); // // We're done with pvData, so free it. // ZeroMemory ( pvData, dwSize ); LocalFree( pvData ); pvData = NULL; } // // If there is no existing information for this entry, create a new one. // if ( pParams == NULL ) { pParams = LocalAlloc(LPTR, sizeof (MPRPARAMSENTRY) ); if (pParams == NULL) { FreeParamsList( ¶mList ); return( ERROR_NOT_ENOUGH_MEMORY ); } InsertTailList( ¶mList, &pParams->ListEntry ); } // // Set the new uid for the entry. // wcscpy( pParams->szPhoneBookEntryName, lpwsInterfaceName ); if ( lpwsUserName != NULL ) { wcscpy( pParams->szUserName, lpwsUserName ); } if ( lpwsPassword != NULL ) { wcscpy( pParams->szPassword, lpwsPassword ); } if ( lpwsDomainName != NULL ) { wcscpy( pParams->szDomain, lpwsDomainName ); } // // Convert the new list back to a string, so we can store it back into // the registry. // pvData = DialParamsListToBlob( ¶mList, &dwSize ); FreeParamsList( ¶mList ); if ( pvData == NULL ) { return( ERROR_NOT_ENOUGH_MEMORY ); } } // // Write it back to the registry. // dwRetCode = WriteDialParamsBlob( lpwsServer, pvData, dwSize ); if ( pvData != NULL ) { SecureZeroMemory( pvData, dwSize ); LocalFree( pvData ); } return( dwRetCode ); } //** // // Call: MprAdminInterfaceGetCredentials // // Returns: NO_ERROR - success // ERROR_INVALID_PARAMETER // // Description: // DWORD APIENTRY MprAdminInterfaceGetCredentialsInternal( IN LPWSTR lpwsServer OPTIONAL, IN LPWSTR lpwsInterfaceName, IN LPWSTR lpwsUserName OPTIONAL, IN LPWSTR lpwsPassword OPTIONAL, IN LPWSTR lpwsDomainName OPTIONAL ) { DWORD dwRetCode; DWORD dwSize; PVOID pvData; LIST_ENTRY paramList; PMPRPARAMSENTRY pParams = NULL; if ( lpwsInterfaceName == NULL ) { return( ERROR_INVALID_PARAMETER ); } if ( ( lpwsUserName == NULL ) && ( lpwsDomainName == NULL ) && ( lpwsPassword == NULL ) ) { return( ERROR_INVALID_PARAMETER ); } // // Read the existing dial params string from the registry. // dwRetCode = ReadDialParamsBlob( lpwsServer, &pvData, &dwSize ); if ( dwRetCode != NO_ERROR ) { return( dwRetCode ); } // // Parse the string into a list, and search for the lpwsInterfaceName entry. // InitializeListHead( ¶mList ); if ( pvData != NULL ) { pParams = DialParamsBlobToList( pvData, lpwsInterfaceName, ¶mList ); // // We're done with pvData, so free it. // ZeroMemory( pvData, dwSize ); LocalFree( pvData ); } // // If the entry doesn't have any saved parameters, then return. // if ( pParams == NULL ) { FreeParamsList( ¶mList ); return( ERROR_CANNOT_FIND_PHONEBOOK_ENTRY ); } // // Otherwise, copy the fields to the caller's buffer. // if ( lpwsUserName != NULL ) { wcscpy( lpwsUserName, pParams->szUserName ); } if ( lpwsPassword != NULL ) { wcscpy( lpwsPassword, pParams->szPassword ); } if ( lpwsDomainName != NULL ) { wcscpy( lpwsDomainName, pParams->szDomain ); } FreeParamsList( ¶mList ); return( NO_ERROR ); }