|
|
///////////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 1998, Microsoft Corp. All rights reserved.
//
// FILE
//
// iasparms.cpp
//
// SYNOPSIS
//
// Defines functions for storing and retrieving (name, value) pairs from
// the SAM UserParameters field.
//
// MODIFICATION HISTORY
//
// 10/16/1998 Original version.
// 02/11/1999 Add RasUser0 functions.
// 02/24/1999 Treat invalid UserParameters as no dialin.
// 03/16/1999 Truncate callback number if too long.
//
///////////////////////////////////////////////////////////////////////////////
#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <windows.h>
#include <stdio.h>
#define IASSAMAPI
#include <iasapi.h>
#include <iasparms.h>
//////////
// I included the Netp function declarations here to avoid dependency on
// the net project.
//////////
DECLSPEC_IMPORT NTSTATUS NTAPI NetpParmsSetUserProperty ( IN LPWSTR UserParms, IN LPWSTR Property, IN UNICODE_STRING PropertyValue, IN WCHAR PropertyFlag, OUT LPWSTR * pNewUserParms, OUT BOOL * Update );
DECLSPEC_IMPORT NTSTATUS NTAPI NetpParmsQueryUserProperty ( IN LPWSTR UserParms, IN LPWSTR Property, OUT PWCHAR PropertyFlag, OUT PUNICODE_STRING PropertyValue );
DECLSPEC_IMPORT VOID NTAPI NetpParmsUserPropertyFree ( LPWSTR NewUserParms );
//////////
// Concatenates two BSTR's and returns the result. The caller is responsible
// for freeing the returned string.
//////////
BSTR WINAPI ConcatentateBSTRs( IN CONST OLECHAR *bstr1, IN CONST OLECHAR *bstr2 ) { UINT len1, len2; BSTR retval;
// Compute the lengths of the two strings.
len1 = bstr1 ? SysStringByteLen((BSTR)bstr1) : 0; len2 = bstr2 ? SysStringByteLen((BSTR)bstr2) : 0;
// Allocate memory for the result.
retval = SysAllocStringByteLen(NULL, len1 + len2);
if (retval) { // Copy in the first string.
if (bstr1) { memcpy(retval, bstr1, len1 + sizeof(WCHAR)); }
// Copy in the second string.
if (bstr2) { memcpy((PBYTE)retval + len1, bstr2, len2 + sizeof(WCHAR)); } }
return retval; }
//////////
// Saves a single-valued VARIANT (i.e., not a SAFEARRAY) to a string.
//////////
HRESULT WINAPI SaveSingleVariantToString( IN CONST VARIANT *pvarSrc, OUT BSTR *pbstrDest ) { HRESULT hr; VARIANT v; UINT len; OLECHAR tag[18]; // 5 + 1 + 10 + 1 + 1
// Coerce the VARIANT to a BSTR.
VariantInit(&v); hr = IASVariantChangeType( &v, (LPVARIANT)pvarSrc, 0, VT_BSTR ); if (FAILED(hr)) { return hr; }
// Compute the length of the header and the data.
len = SysStringLen(V_BSTR(&v)); len += swprintf(tag, L"%hu:%lu:", V_VT(pvarSrc), len);
// Allocate the result string.
*pbstrDest = SysAllocStringLen(NULL, len); if (*pbstrDest != NULL) { // Copy in the tag and the data.
wcscat(wcscpy(*pbstrDest, tag), V_BSTR(&v)); } else { hr = E_OUTOFMEMORY; }
// Clear the intermediate string.
VariantClear(&v);
return hr; }
//////////
// Loads a single-valued VARIANT (i.e., not a SAFEARRAY) from a string.
// Also returns a pointer to where the scan stopped.
//////////
HRESULT WINAPI LoadSingleVariantFromString( IN PCWSTR pszSrc, IN UINT cSrcLen, OUT VARIANT *pvarDest, OUT PCWSTR *ppszEnd ) { PCWSTR nptr; VARTYPE vt; PWSTR endptr; ULONG len; VARIANT v; HRESULT hr;
// Initialize the cursor.
nptr = pszSrc;
// Read the VARTYPE token.
vt = (VARTYPE)wcstoul(nptr, &endptr, 10); if (endptr == nptr || *endptr != L':') { return E_INVALIDARG; } nptr = endptr + 1;
// Read the length token.
len = wcstoul(nptr, &endptr, 10); if (endptr == nptr || *endptr != L':') { return E_INVALIDARG; } nptr = endptr + 1;
// Make sure there's enough characters left for the data.
if (nptr + len > pszSrc + cSrcLen) { return E_INVALIDARG; }
// Read the BSTR data into a VARIANT.
V_VT(&v) = VT_BSTR; V_BSTR(&v) = SysAllocStringLen(nptr, len); if (V_BSTR(&v) == NULL) { return E_OUTOFMEMORY; }
// Coerce the VARIANT to the desired type.
hr = IASVariantChangeType( pvarDest, &v, 0, vt );
// Clear the intermediate string.
VariantClear(&v);
// Return the position where the scan stopped.
*ppszEnd = nptr + len;
return hr; }
//////////
// Saves a VARIANT to a string. The caller is responsible for freeing the
// returned string.
//////////
HRESULT WINAPI IASSaveVariantToString( IN CONST VARIANT *pvarSrc, OUT BSTR *pbstrDest ) { HRESULT hr; SAFEARRAY *psa; LONG lowerBound, upperBound, idx; VARIANT *data; BSTR item, newResult;
// Check the input arguments.
if (pvarSrc == NULL || pbstrDest == NULL) { return E_POINTER; }
// Initialize the return parameter.
*pbstrDest = NULL;
// Is this an array ?
if (V_VT(pvarSrc) != (VT_VARIANT | VT_ARRAY)) { // No, so we can delegate and bail.
return SaveSingleVariantToString(pvarSrc, pbstrDest); }
// Yes, so extract the SAFEARRAY.
psa = V_ARRAY(pvarSrc);
// We only handle one-dimensional arrays.
if (SafeArrayGetDim(psa) != 1) { return DISP_E_TYPEMISMATCH; }
// Get the array bounds.
hr = SafeArrayGetLBound(psa, 1, &lowerBound); if (FAILED(hr)) { return hr; } hr = SafeArrayGetUBound(psa, 1, &upperBound); if (FAILED(hr)) { return hr; }
// Get the embedded array of VARIANTs.
hr = SafeArrayAccessData(psa, (PVOID*)&data);
// Loop through each VARIANT in the array.
for (idx = lowerBound; idx <= upperBound; ++idx, ++data) { // Save the VARIANT into a BSTR.
hr = SaveSingleVariantToString(data, &item); if (FAILED(hr)) { break; }
// Merge this into the result ...
newResult = ConcatentateBSTRs(*pbstrDest, item);
// ... and free the old strings.
SysFreeString(*pbstrDest); SysFreeString(item);
// Store the new result.
*pbstrDest = newResult;
if (!newResult) { hr = E_OUTOFMEMORY; break; } }
// If anything went wrong, clean-up the partial result.
if (FAILED(hr)) { SysFreeString(*pbstrDest); *pbstrDest = NULL; }
// Unlock the array.
SafeArrayUnaccessData(psa);
return hr; }
//////////
// Loads a VARIANT from a string. The caller is responsible for freeing the
// returned VARIANT.
//////////
HRESULT WINAPI IASLoadVariantFromString( IN PCWSTR pszSrc, IN UINT cSrcLen, OUT VARIANT *pvarDest ) { PCWSTR end; HRESULT hr; SAFEARRAYBOUND bound; SAFEARRAY *psa; LONG index; VARIANT* item;
// Check the parameters.
if (pszSrc == NULL || pvarDest == NULL) { return E_POINTER; }
// Initialize the out parameter.
VariantInit(pvarDest);
// Compute the end of the buffer.
end = pszSrc + cSrcLen;
// Go for the quick score on a single-valued property.
hr = LoadSingleVariantFromString( pszSrc, cSrcLen, pvarDest, &pszSrc ); if (FAILED(hr) || pszSrc == end) { return hr; }
// Create a SAFEARRAY of VARIANTs to hold the array elements.
// We know we have at least two elements.
bound.cElements = 2; bound.lLbound = 0; psa = SafeArrayCreate(VT_VARIANT, 1, &bound); if (psa == NULL) { VariantClear(pvarDest); return E_OUTOFMEMORY; }
// Store the VARIANT we already converted.
index = 0; SafeArrayPtrOfIndex(psa, &index, (PVOID*)&item); memcpy(item, pvarDest, sizeof(VARIANT));
// Now put the SAFEARRAY into the returned VARIANT.
V_VT(pvarDest) = VT_ARRAY | VT_VARIANT; V_ARRAY(pvarDest) = psa;
do { // Get the next element in the array.
++index; hr = SafeArrayPtrOfIndex(psa, &index, (PVOID*)&item); if (FAILED(hr)) { break; }
// Load the next value.
hr = LoadSingleVariantFromString( pszSrc, (UINT)(end - pszSrc), item, &pszSrc ); if (FAILED(hr) || pszSrc == end) { break; }
// We must have at least one more element, so grow the array.
++bound.cElements; hr = SafeArrayRedim(psa, &bound);
} while (SUCCEEDED(hr));
// If we failed, clean-up any partial results.
if (FAILED(hr)) { VariantClear(pvarDest); }
return hr; }
HRESULT WINAPI IASParmsSetUserProperty( IN PCWSTR pszUserParms, IN PCWSTR pszName, IN CONST VARIANT *pvarValue, OUT PWSTR *ppszNewUserParms ) { BSTR bstrValue; UNICODE_STRING uniValue; NTSTATUS status; HRESULT hr; BOOL update;
// Check the parameters.
if (pvarValue == NULL || ppszNewUserParms == NULL) { return E_POINTER; }
// Initialize the out parameter.
*ppszNewUserParms = NULL;
// Is the VARIANT empty ?
if (V_VT(pvarValue) != VT_EMPTY) { // No, so save it to a string.
hr = IASSaveVariantToString( pvarValue, &bstrValue ); if (FAILED(hr)) { return hr; }
RtlInitUnicodeString(&uniValue, bstrValue); } else { // Yes, so we're actually going to erase the property.
bstrValue = NULL; memset(&uniValue, 0, sizeof(UNICODE_STRING)); }
// Write the property to UserParms.
status = NetpParmsSetUserProperty( (PWSTR)pszUserParms, (PWSTR)pszName, uniValue, 0, ppszNewUserParms, &update );
if (NT_SUCCESS(status)) { hr = S_OK; } else { status = RtlNtStatusToDosError(status); hr = HRESULT_FROM_WIN32(status); }
// Free the BSTR value.
SysFreeString(bstrValue);
return hr; }
HRESULT WINAPI IASParmsQueryUserProperty( IN PCWSTR pszUserParms, IN PCWSTR pszName, OUT VARIANT *pvarValue ) { NTSTATUS status; HRESULT hr; WCHAR flag; UNICODE_STRING uniValue;
// Check the parameters.
if (pvarValue == NULL) { return E_POINTER; }
// Initialize the out parameter.
VariantInit(pvarValue);
// Get the property from UserParms.
status = NetpParmsQueryUserProperty( (PWSTR)pszUserParms, (PWSTR)pszName, &flag, &uniValue ); if (NT_SUCCESS(status)) { if (uniValue.Buffer != NULL) { // We got a string so convert it to a VARIANT ...
hr = IASLoadVariantFromString( uniValue.Buffer, uniValue.Length / sizeof (WCHAR), pvarValue );
// ... and free the string.
LocalFree(uniValue.Buffer); } else { // Buffer is zero-length, so we return VT_EMPTY.
hr = S_OK; } } else { status = RtlNtStatusToDosError(status); hr = HRESULT_FROM_WIN32(status); }
return hr; }
VOID WINAPI IASParmsFreeUserParms( IN PWSTR pszNewUserParms ) { LocalFree(pszNewUserParms); }
/////////
// Constants used for compressing/decompressing phone numbers.
/////////
CONST WCHAR COMPRESS_MAP[] = L"() tTpPwW,-@*#";
#define UNPACKED_DIGIT (100)
#define COMPRESS_MAP_BEGIN (110)
#define COMPRESS_MAP_END (COMPRESS_MAP_BEGIN + 14)
#define UNPACKED_OTHER (COMPRESS_MAP_END + 1)
///////////////////////////////////////////////////////////////////////////////
//
// FUNCTION
//
// CompressPhoneNumber
//
// DESCRIPTION
//
// Bizarre algorithm used to compress phone numbers stored in the
// usr_parms field.
//
///////////////////////////////////////////////////////////////////////////////
VOID WINAPI CompressPhoneNumber( IN PCWSTR uncompressed, OUT PWSTR compressed ) { BOOL packed = FALSE;
for( ; *uncompressed; ++uncompressed) { switch (*uncompressed) { case L'0':
if (packed) { // Put zero as the second paired digit
if (*compressed) { *compressed *= 10; ++compressed; packed = FALSE; } else { // We have a zero, we cant put a second zero or that
// will be a null byte. So, we store the value
// UNPACKED_DIGIT to fake this.
*compressed = UNPACKED_DIGIT; *(++compressed) = 0; packed = TRUE; } } else { *compressed = 0; packed = TRUE; }
break;
case L'1': case L'2': case L'3': case L'4': case L'5': case L'6': case L'7': case L'8': case L'9':
// If this is the second digit that is going to be
// packed into one byte
if (packed) { *compressed *= 10; *compressed += *uncompressed - L'0';
// we need to special case number 32 which maps to a blank
if (*compressed == L' ') { *compressed = COMPRESS_MAP_END; }
++compressed; packed = FALSE; } else { *compressed = *uncompressed - '0'; packed = TRUE; }
break;
case L'(': case L')': case L' ': case L't': case L'T': case L'p': case L'P': case L'w': case L'W': case L',': case L'-': case L'@': case L'*': case L'#':
// if the byte was packed then we unpack it
if (packed) { *compressed += UNPACKED_DIGIT; ++compressed; packed = FALSE; }
*compressed = (WCHAR)(COMPRESS_MAP_BEGIN + (wcschr(COMPRESS_MAP, *uncompressed) - COMPRESS_MAP)); ++compressed; break;
default:
// if the chracter is none of the above specially recognized
// characters then copy the value + UNPACKED_OTHER to make it
// possible to decompress at the other end. [ 6/4/96 RamC ]
if (packed) { *compressed += UNPACKED_DIGIT; ++compressed; packed = FALSE; }
*compressed = *uncompressed + UNPACKED_OTHER; ++compressed; } }
// If we are in the middle of packing something then we unpack it.
if (packed) { *compressed += UNPACKED_DIGIT; ++compressed; }
// Add the null terminator.
*compressed = L'\0'; }
///////////////////////////////////////////////////////////////////////////////
//
// FUNCTION
//
// DecompressPhoneNumber
//
// DESCRIPTION
//
// The inverse of CompressPhoneNumber above.
//
///////////////////////////////////////////////////////////////////////////////
VOID WINAPI DecompressPhoneNumber( IN PCWSTR compressed, OUT PWSTR decompressed ) { for( ; *compressed; ++compressed, ++decompressed) { // If this character is packed, then we unpack it.
if (*compressed < UNPACKED_DIGIT) { *decompressed = *compressed / 10 + L'0'; ++decompressed; *decompressed = *compressed % 10 + L'0'; continue; }
// We need to special case number 32 which maps to a blank.
if (*compressed == COMPRESS_MAP_END) { *decompressed = L'3'; ++decompressed; *decompressed = L'2'; continue; }
// The character is an unpacked digit.
if (*compressed < COMPRESS_MAP_BEGIN) { *decompressed = *compressed - UNPACKED_DIGIT + L'0'; continue; }
// The character is from the compression map.
if (*compressed < UNPACKED_OTHER) { *decompressed = COMPRESS_MAP[*compressed - COMPRESS_MAP_BEGIN]; continue; }
// Otherwise the character is unpacked.
*decompressed = *compressed - UNPACKED_OTHER; }
// Add a null terminator.
*decompressed = L'\0'; }
/////////
// Definition of the downlevel UserParameters.
/////////
#define UP_CLIENT_MAC (L'm')
#define UP_CLIENT_DIAL (L'd')
#define UP_LEN_MAC (LM20_UNLEN)
#define UP_LEN_DIAL (LM20_MAXCOMMENTSZ - 4 - UP_LEN_MAC)
typedef struct { WCHAR up_MACid; WCHAR up_PriGrp[UP_LEN_MAC]; WCHAR up_MAC_Terminator; WCHAR up_DIALid; WCHAR up_Privilege; WCHAR up_CBNum[UP_LEN_DIAL]; } USER_PARMS;
#define USER_PARMS_LEN (sizeof(USER_PARMS)/sizeof(WCHAR))
///////////////////////////////////////////////////////////////////////////////
//
// FUNCTION
//
// InitUserParms
//
// DESCRIPTION
//
// Initializes a USER_PARMS struct to a valid default state.
//
///////////////////////////////////////////////////////////////////////////////
VOID WINAPI InitUserParms( IN USER_PARMS* userParms ) { WCHAR *i, *end;
// Set everything to a space ' '.
i = (PWCHAR)userParms; end = i + USER_PARMS_LEN; for ( ; i != end; ++i) { *i = L' '; }
// Initialize the 'special' fields.
userParms->up_MACid = UP_CLIENT_MAC; userParms->up_PriGrp[0] = L':'; userParms->up_DIALid = UP_CLIENT_DIAL; userParms->up_Privilege = RASPRIV_NoCallback; }
///////////////////////////////////////////////////////////////////////////////
//
// FUNCTION
//
// IASParmsSetRasUser0
//
// DESCRIPTION
//
// Encodes the RAS_USER_0 struct into the downlevel portion of the
// UserParameters string.
//
///////////////////////////////////////////////////////////////////////////////
DWORD WINAPI IASParmsSetRasUser0( IN OPTIONAL PCWSTR pszOldUserParms, IN CONST RAS_USER_0* pRasUser0, OUT PWSTR* ppszNewUserParms ) { size_t oldLen, newLen, compressedLen; USER_PARMS userParms; WCHAR compressed[MAX_PHONE_NUMBER_LEN + 1];
// Check the pointers.
if (pRasUser0 == NULL || ppszNewUserParms == NULL) { return ERROR_INVALID_PARAMETER; }
// Initialize the out parameters.
*ppszNewUserParms = NULL;
// Determine the length of the old UserParameters.
oldLen = pszOldUserParms ? wcslen(pszOldUserParms) : 0;
// Initialize the USER_PARMS structure.
InitUserParms(&userParms);
// Preserve the MAC Primary Group if present.
if (oldLen > UP_LEN_MAC) { memcpy( userParms.up_PriGrp, pszOldUserParms + 1, sizeof(userParms.up_PriGrp) ); }
// Validate the CallbackType and save the compressed phone number.
switch (pRasUser0->bfPrivilege & RASPRIV_CallbackType) { case RASPRIV_NoCallback: case RASPRIV_AdminSetCallback: case RASPRIV_CallerSetCallback: {
// Compress the phone number.
CompressPhoneNumber(pRasUser0->wszPhoneNumber, compressed);
// Make sure it will fit in USER_PARMS.
compressedLen = wcslen(compressed); if (compressedLen > UP_LEN_DIAL) { compressedLen = UP_LEN_DIAL; }
// Store the compressed phone number.
memcpy(userParms.up_CBNum, compressed, compressedLen * sizeof(WCHAR));
break; }
default: return ERROR_BAD_FORMAT; }
// Store the privilege flags.
userParms.up_Privilege = pRasUser0->bfPrivilege;
// Allocate memory for the new UserParameters.
newLen = max(oldLen, USER_PARMS_LEN); *ppszNewUserParms = (PWSTR)LocalAlloc( LMEM_FIXED, (newLen + 1) * sizeof(WCHAR) ); if (*ppszNewUserParms == NULL) { return ERROR_NOT_ENOUGH_MEMORY; }
// Copy in the USER_PARMS struct.
memcpy(*ppszNewUserParms, &userParms, sizeof(USER_PARMS));
// Copy in any extra stuff.
if (oldLen > USER_PARMS_LEN) { memcpy( *ppszNewUserParms + USER_PARMS_LEN, pszOldUserParms + USER_PARMS_LEN, (oldLen - USER_PARMS_LEN) * sizeof(WCHAR) ); }
// Add the null terminator.
*(*ppszNewUserParms + newLen) = L'\0';
return NO_ERROR; }
///////////////////////////////////////////////////////////////////////////////
//
// FUNCTION
//
// IASParmsQueryRasUser0
//
// DESCRIPTION
//
// Decodes the RAS_USER_0 struct from the UserParameters string.
//
///////////////////////////////////////////////////////////////////////////////
DWORD WINAPI IASParmsQueryRasUser0( IN OPTIONAL PCWSTR pszUserParms, OUT PRAS_USER_0 pRasUser0 ) { USER_PARMS* usrp; WCHAR callbackNumber[UP_LEN_DIAL + 1], *p;
// Check the pointers.
if (pRasUser0 == NULL) { return ERROR_INVALID_PARAMETER; }
// Cast the string buffer to a USER_PARMS struct.
usrp = (USER_PARMS*)pszUserParms;
// If parms is not properly initialized, default to no RAS privilege.
if (!pszUserParms || wcslen(pszUserParms) < USER_PARMS_LEN || usrp->up_DIALid != UP_CLIENT_DIAL) { pRasUser0->bfPrivilege = RASPRIV_NoCallback; pRasUser0->wszPhoneNumber[0] = L'\0'; return NO_ERROR; }
// Make a local copy.
memcpy(callbackNumber, usrp->up_CBNum, sizeof(WCHAR) * UP_LEN_DIAL);
// Add a null terminator and null out any trailing blanks.
p = callbackNumber + UP_LEN_DIAL; *p = L'\0'; while (--p >= callbackNumber && *p == L' ') { *p = L'\0'; }
// Sanity check the bfPrivilege field.
switch(usrp->up_Privilege & RASPRIV_CallbackType) { case RASPRIV_NoCallback: case RASPRIV_AdminSetCallback: case RASPRIV_CallerSetCallback: { pRasUser0->bfPrivilege = (BYTE)usrp->up_Privilege; DecompressPhoneNumber(callbackNumber, pRasUser0->wszPhoneNumber); break; }
default: { pRasUser0->bfPrivilege = RASPRIV_NoCallback; pRasUser0->wszPhoneNumber[0] = L'\0'; } }
return NO_ERROR; }
|