/* File userdb.c Implementation of the local user database object. Paul Mayfield, 10/8/97 */ #include "rassrv.h" // Registry values extern WCHAR pszregRasParameters[]; extern WCHAR pszregServerFlags[]; extern WCHAR pszregPure[]; // Cached values for users typedef struct _RASSRV_USERINFO { HANDLE hUser; // Handle to user PWCHAR pszName; PWCHAR pszFullName; // Only loaded if requested //gangz for secure password bug //Change this for the new safe encoding password functions WCHAR szPassword[PWLEN+1]; // Only non-null if this is new password to be committed WCHAR wszPhoneNumber[MAX_PHONE_NUMBER_LEN + 1]; BYTE bfPrivilege; BYTE bDirty; } RASSRV_USERINFO; // Structure used to implement/manipulate the local user database typedef struct _RASSRV_USERDB { HANDLE hServer; // Handle to user server DWORD dwUserCount; // Number of users in the database DWORD dwCacheSize; // Number of users can be stored in cache BOOL bEncrypt; // Whether encryption should be used BOOL bDccBypass; // Whether dcc connections can bypass auth. BOOL bPure; // Whether database is "Pure" BOOL bEncSettingLoaded; // Whether we've read in the enc setting BOOL bFlushOnClose; RASSRV_USERINFO ** pUserCache; // Cache of users } RASSRV_USERDB; // Defines a callback for enumerating users. Returns TRUE to continue the enueration // FALSE to stop it. typedef BOOL (* pEnumUserCb)( IN NET_DISPLAY_USER* pUser, IN HANDLE hData); // We use this to guess the size of the the user array // (so we can grow it when new users are added) #define USR_ARRAY_GROW_SIZE 50 // Dirty flags #define USR_RASPROPS_DIRTY 0x1 // whether callback is dirty #define USR_FULLNAME_DIRTY 0x2 // whether full name needs to be flushed #define USR_PASSWORD_DIRTY 0x4 // whether password needs to be flushed #define USR_ADD_DIRTY 0x8 // whether user needs to be added // Helper macros for dealing with dirty flags #define usrDirtyRasProps(pUser) ((pUser)->bDirty |= USR_RASPROPS_DIRTY) #define usrDirtyFullname(pUser) ((pUser)->bDirty |= USR_FULLNAME_DIRTY) #define usrDirtyPassword(pUser) ((pUser)->bDirty |= USR_PASSWORD_DIRTY) #define usrDirtyAdd(pUser) ((pUser)->bDirty |= USR_ADD_DIRTY) #define usrIsDirty(pUser) ((pUser)->bDirty) #define usrIsRasPropsDirty(pUser) ((pUser)->bDirty & USR_RASPROPS_DIRTY) #define usrIsFullNameDirty(pUser) ((pUser)->bDirty & USR_FULLNAME_DIRTY) #define usrIsPasswordDirty(pUser) ((pUser)->bDirty & USR_PASSWORD_DIRTY) #define usrIsAddDirty(pUser) ((pUser)->bDirty & USR_ADD_DIRTY) #define usrClearDirty(pUser) ((pUser)->bDirty = 0) #define usrClearRasPropsDirty(pUser) ((pUser)->bDirty &= ~USR_CALLBACK_DIRTY) #define usrClearFullNameDirty(pUser) ((pUser)->bDirty &= ~USR_FULLNAME_DIRTY) #define usrClearPasswordDirty(pUser) ((pUser)->bDirty &= ~USR_PASSWORD_DIRTY) #define usrClearAddDirty(pUser) ((pUser)->bDirty &= ~USR_ADD_DIRTY) #define usrFlagIsSet(_val, _flag) (((_val) & (_flag)) != 0) #define usrFlagIsClear(_val, _flag) (((_val) & (_flag)) == 0) // // Reads in server flags and determines whether encrypted // password and data are required. // // lpdwFlags is assigned one of the following on success // 0 = data and pwd enc not required // MPR_USER_PROF_FLAG_SECURE = data and pwd enc required // MPR_USER_PROF_FLAG_UNDETERMINED = Can't say for sure // DWORD usrGetServerEnc( OUT LPDWORD lpdwFlags) { DWORD dwFlags = 0; if (!lpdwFlags) return ERROR_INVALID_PARAMETER; // Read in the flags RassrvRegGetDw(&dwFlags, 0, (const PWCHAR)pszregRasParameters, (const PWCHAR)pszregServerFlags); // The following bits will be set for secure auth. // if ( (usrFlagIsSet (dwFlags, PPPCFG_NegotiateMSCHAP)) && (usrFlagIsSet (dwFlags, PPPCFG_NegotiateStrongMSCHAP)) && (usrFlagIsClear (dwFlags, PPPCFG_NegotiateMD5CHAP)) && (usrFlagIsClear (dwFlags, PPPCFG_NegotiateSPAP)) && (usrFlagIsClear (dwFlags, PPPCFG_NegotiateEAP)) && (usrFlagIsClear (dwFlags, PPPCFG_NegotiatePAP)) ) { *lpdwFlags = MPR_USER_PROF_FLAG_SECURE; return NO_ERROR; } // The following bits will be set for insecure auth. // else if ( (usrFlagIsSet (dwFlags, PPPCFG_NegotiateMSCHAP)) && (usrFlagIsSet (dwFlags, PPPCFG_NegotiateStrongMSCHAP)) && (usrFlagIsSet (dwFlags, PPPCFG_NegotiateSPAP)) && (usrFlagIsSet (dwFlags, PPPCFG_NegotiatePAP)) && (usrFlagIsClear (dwFlags, PPPCFG_NegotiateEAP)) && (usrFlagIsClear (dwFlags, PPPCFG_NegotiateMD5CHAP)) ) { *lpdwFlags = 0; // data and pwd enc not required return NO_ERROR; } // Otherwise, we are undetermined *lpdwFlags = MPR_USER_PROF_FLAG_UNDETERMINED; return NO_ERROR; } // // Sets the encryption policy for the server // DWORD usrSetServerEnc( IN DWORD dwFlags) { DWORD dwSvrFlags = 0; // Read in the old flags RassrvRegGetDw(&dwSvrFlags, 0, (const PWCHAR)pszregRasParameters, (const PWCHAR)pszregServerFlags); // If the user requires encryption then set MSCHAP // and CHAP as the only authentication types and // set the ipsec flag. if (dwFlags & MPR_USER_PROF_FLAG_SECURE) { dwSvrFlags |= PPPCFG_NegotiateMSCHAP; dwSvrFlags |= PPPCFG_NegotiateStrongMSCHAP; dwSvrFlags &= ~PPPCFG_NegotiateMD5CHAP; dwSvrFlags &= ~PPPCFG_NegotiateSPAP; dwSvrFlags &= ~PPPCFG_NegotiateEAP; dwSvrFlags &= ~PPPCFG_NegotiatePAP; } // Otherwise, the user does require encryption, // so enable all authentication types and disable // the requirement to use IPSEC else { dwSvrFlags &= ~PPPCFG_NegotiateMD5CHAP; dwSvrFlags &= ~PPPCFG_NegotiateEAP; dwSvrFlags |= PPPCFG_NegotiateMSCHAP; dwSvrFlags |= PPPCFG_NegotiateStrongMSCHAP; dwSvrFlags |= PPPCFG_NegotiateSPAP; dwSvrFlags |= PPPCFG_NegotiatePAP; } // Commit changes to the registry RassrvRegSetDw(dwSvrFlags, (const PWCHAR)pszregRasParameters, (const PWCHAR)pszregServerFlags); return NO_ERROR; } // Enumerates the local users // DWORD usrEnumLocalUsers( IN pEnumUserCb pCbFunction, IN HANDLE hData) { DWORD dwErr, dwIndex = 0, dwCount = 100, dwEntriesRead, i; NET_DISPLAY_USER * pUsers; NET_API_STATUS nStatus; RAS_USER_0 RasUser0; HANDLE hUser = NULL, hServer = NULL; // Enumerate the users, while (TRUE) { // Read in the first block of user names nStatus = NetQueryDisplayInformation( NULL, 1, dwIndex, dwCount, dwCount * sizeof(NET_DISPLAY_USER), &dwEntriesRead, &pUsers); // Get out if there's an error getting user names if ((nStatus != NERR_Success) && (nStatus != ERROR_MORE_DATA)) { break; } // For each user read in, call the callback function for (i = 0; i < dwEntriesRead; i++) { BOOL bOk; //For whistler bug 243874 gangz //On whistler Personal version, we wont show the Administrator //in the user's listview on the Incoming connection's User Tab // if ( (DOMAIN_USER_RID_ADMIN == pUsers[i].usri1_user_id) && IsPersonalPlatform() ) { continue; } bOk = (*pCbFunction)(&(pUsers[i]), hData); if (bOk == FALSE) { nStatus = NERR_Success; break; } } // Set the index to read in the next set of users dwIndex = pUsers[dwEntriesRead - 1].usri1_next_index; // Free the users buffer NetApiBufferFree (pUsers); // If we've read in everybody, go ahead and break if (nStatus != ERROR_MORE_DATA) { break; } } return NO_ERROR; } // Copies the data in pRassrvUser to its equivalent in UserInfo DWORD usrSyncRasProps( IN RASSRV_USERINFO * pRassrvUser, OUT RAS_USER_0 * UserInfo) { UserInfo->bfPrivilege = pRassrvUser->bfPrivilege; lstrcpynW( UserInfo->wszPhoneNumber, pRassrvUser->wszPhoneNumber, MAX_PHONE_NUMBER_LEN); UserInfo->wszPhoneNumber[MAX_PHONE_NUMBER_LEN] = (WCHAR)0; return NO_ERROR; } // Commits the data for the given user to the local user database DWORD usrCommitRasProps( IN RASSRV_USERINFO * pRassrvUser) { DWORD dwErr = NO_ERROR; RAS_USER_0 UserInfo; dwErr = usrSyncRasProps(pRassrvUser, &UserInfo); if (dwErr != NO_ERROR) return dwErr; dwErr = MprAdminUserWrite(pRassrvUser->hUser, 0, (LPBYTE)&UserInfo); if (dwErr != NO_ERROR) DbgOutputTrace ("usrCommitRasProps: unable to commit %S (0x%08x)", pRassrvUser->pszName, dwErr); return dwErr; } // Simple bounds checking BOOL usrBoundsCheck( IN RASSRV_USERDB * This, IN DWORD dwIndex) { // Dwords are unsigned, so no need to check < 0 if (This->dwUserCount <= dwIndex) return FALSE; return TRUE; } // Frees an array of users DWORD usrFreeUserArray( IN RASSRV_USERINFO ** pUsers, IN DWORD dwCount) { DWORD i; if (!pUsers) return ERROR_INVALID_PARAMETER; for (i=0; i < dwCount; i++) { if (pUsers[i]) { if (pUsers[i]->hUser) MprAdminUserClose(pUsers[i]->hUser); if (pUsers[i]->pszName) RassrvFree (pUsers[i]->pszName); if (pUsers[i]->pszFullName) RassrvFree (pUsers[i]->pszFullName); //Wipe password before free memory, if CryptProtectData() is used //this will also release the memory allocated by it SafeWipePasswordBuf(pUsers[i]->szPassword); RassrvFree(pUsers[i]); } } return NO_ERROR; } // Standard user comparison function used for sorting int _cdecl usrCompareUsers( IN const void * elem1, IN const void * elem2) { RASSRV_USERINFO* p1 = *((RASSRV_USERINFO**)elem1); RASSRV_USERINFO* p2 = *((RASSRV_USERINFO**)elem2); return lstrcmpi(p1->pszName, p2->pszName); } // Returns whether a given user exists BOOL usrUserExists ( IN RASSRV_USERDB * This, IN PWCHAR pszName) { DWORD i; int iCmp; for (i = 0; i < This->dwUserCount; i++) { iCmp = lstrcmpi(This->pUserCache[i]->pszName, pszName); if (iCmp == 0) return TRUE; if (iCmp > 0) return FALSE; } return FALSE; } // Resorts the cache DWORD usrResortCache( IN RASSRV_USERDB * This) { qsort( This->pUserCache, This->dwUserCount, sizeof(RASSRV_USERINFO*), usrCompareUsers); return NO_ERROR; } // Resizes the user cache to allow for added users DWORD usrResizeCache( IN RASSRV_USERDB * This, IN DWORD dwNewSize) { RASSRV_USERINFO ** pNewCache; DWORD i; // Only resize bigger (this could be changed) if ((!This) || (dwNewSize <= This->dwCacheSize)) return ERROR_INVALID_PARAMETER; // Allocate the new cache pNewCache = RassrvAlloc(dwNewSize * sizeof (RASSRV_USERINFO*), TRUE); if (pNewCache == NULL) return ERROR_NOT_ENOUGH_MEMORY; // Copy over the old entries and free the old cache if (This->pUserCache) { CopyMemory( (PVOID)pNewCache, (CONST VOID *)(This->pUserCache), This->dwCacheSize * sizeof(RASSRV_USERINFO*)); RassrvFree(This->pUserCache); } // Reassign the new cache and update the cache size This->pUserCache = pNewCache; This->dwCacheSize = dwNewSize; return NO_ERROR; } // Enumeration callback that adds users to the local database // as they are read from the system. BOOL usrInitializeUser( NET_DISPLAY_USER * pNetUser, HANDLE hUserDatabase) { RASSRV_USERDB * This = (RASSRV_USERDB*)hUserDatabase; RASSRV_USERINFO * pRasUser = NULL; DWORD dwErr = NO_ERROR, dwSize; RAS_USER_0 UserInfo; // Make sure we have a valid database if (!This) { return FALSE; } // Resize the cache to accomodate more users if needed if (This->dwUserCount >= This->dwCacheSize) { dwErr = usrResizeCache( This, This->dwCacheSize + USR_ARRAY_GROW_SIZE); if (dwErr != NO_ERROR) { return FALSE; } } // Allocate this user pRasUser = RassrvAlloc(sizeof(RASSRV_USERINFO), TRUE); if (pRasUser == NULL) { return FALSE; } do { // Point to the user name dwSize = (wcslen(pNetUser->usri1_name) + 1) * sizeof(WCHAR); pRasUser->pszName = RassrvAlloc(dwSize, FALSE); if (!pRasUser->pszName) { dwErr = ERROR_NOT_ENOUGH_MEMORY; break; } wcscpy(pRasUser->pszName, pNetUser->usri1_name); // Open the user handle dwErr = MprAdminUserOpen ( This->hServer, pRasUser->pszName, &(pRasUser->hUser)); if (dwErr != NO_ERROR) { break; } // Get the ras user info dwErr = MprAdminUserRead(pRasUser->hUser, 0, (LPBYTE)&UserInfo); if (dwErr != NO_ERROR) { break; } // Clear any dirty flags usrClearDirty(pRasUser); // Copy the phone number lstrcpynW( pRasUser->wszPhoneNumber, UserInfo.wszPhoneNumber, MAX_PHONE_NUMBER_LEN); pRasUser->wszPhoneNumber[MAX_PHONE_NUMBER_LEN] = (WCHAR)0; // Copy the privelege flags pRasUser->bfPrivilege = UserInfo.bfPrivilege; // Assign the user in the cache This->pUserCache[This->dwUserCount] = pRasUser; // Update the user count This->dwUserCount += 1; } while (FALSE); // Cleanup { if (dwErr != NO_ERROR) { if (pRasUser) { if (pRasUser->pszName) { RassrvFree(pRasUser->pszName); } RassrvFree(pRasUser); } } } return (dwErr == NO_ERROR) ? TRUE : FALSE; } // // Loads the global encryption setting. Because the operation opens // up .mdb files to read profiles, etc. it is put in its own function // and is called only when absolutely needed. // DWORD usrLoadEncryptionSetting( IN RASSRV_USERDB * This) { DWORD dwErr = NO_ERROR; DWORD dwSvrFlags, dwProfFlags; if (This->bEncSettingLoaded) { return NO_ERROR; } // Read in the encryption setting by combining the // server flags with the values in the default // profile. dwSvrFlags = MPR_USER_PROF_FLAG_UNDETERMINED; dwProfFlags = MPR_USER_PROF_FLAG_UNDETERMINED; MprAdminUserReadProfFlags (This->hServer, &dwProfFlags); usrGetServerEnc (&dwSvrFlags); // If both sources confirm the encryption requirement // then we require encryption if ((dwProfFlags & MPR_USER_PROF_FLAG_SECURE) && (dwSvrFlags & MPR_USER_PROF_FLAG_SECURE)) { This->bEncrypt = TRUE; } else { This->bEncrypt = FALSE; } This->bEncSettingLoaded = TRUE; return dwErr; } // Creates a user data base object, initializing it from the local // user database and returning a handle to it. DWORD usrOpenLocalDatabase ( IN HANDLE * hUserDatabase) { RASSRV_USERDB * This = NULL; DWORD dwErr; if (!hUserDatabase) return ERROR_INVALID_PARAMETER; // Allocate the database if ((This = RassrvAlloc(sizeof(RASSRV_USERDB), TRUE)) == NULL) return ERROR_NOT_ENOUGH_MEMORY; // Connect to the user server dwErr = MprAdminUserServerConnect(NULL, TRUE, &(This->hServer)); if (dwErr != NO_ERROR) { RassrvFree(This); return dwErr; } // Load in the data from the system if ((dwErr = usrReloadLocalDatabase((HANDLE)This)) == NO_ERROR) { *hUserDatabase = (HANDLE)This; This->bFlushOnClose = FALSE; return NO_ERROR; } DbgOutputTrace ("usrOpenLocalDb: unable to load user db 0x%08x", dwErr); RassrvFree(This); *hUserDatabase = NULL; return dwErr; } // Reloads the user information cached in the user database obj // from the system. This can be used to implement a refresh in the ui. // DWORD usrReloadLocalDatabase ( IN HANDLE hUserDatabase) { RASSRV_USERDB * This = (RASSRV_USERDB*)hUserDatabase; DWORD dwErr; // Validate if (!This) { return ERROR_INVALID_PARAMETER; } // Cleanup the old database if (This->pUserCache) { usrFreeUserArray(This->pUserCache, This->dwUserCount); RassrvFree(This->pUserCache); } // The encryption setting is loaded on demand from the // usrGetEncryption/usrSetEncryption api's. This is a performance // tune so that the IC wizard wouldn't have to wait for // the profile to be loaded even though it doesn't use the result. // Read in the purity of the system { DWORD dwPure = 0; RassrvRegGetDw(&dwPure, 0, (const PWCHAR)pszregRasParameters, (const PWCHAR)pszregPure); if (dwPure == 1) This->bPure = FALSE; else This->bPure = TRUE; } // Read in whether dcc connections can be bypassed { DWORD dwSvrFlags = 0; RassrvRegGetDw( &dwSvrFlags, dwSvrFlags, (const PWCHAR)pszregRasParameters, (const PWCHAR)pszregServerFlags); if (dwSvrFlags & PPPCFG_AllowNoAuthOnDCPorts) This->bDccBypass = TRUE; else This->bDccBypass = FALSE; } // Enumerate the local users from the system adding them // to this database. dwErr = usrEnumLocalUsers(usrInitializeUser, hUserDatabase); if (dwErr != NO_ERROR) { return NO_ERROR; } return NO_ERROR; } // Frees up the resources held by a user database object. DWORD usrCloseLocalDatabase ( IN HANDLE hUserDatabase) { DWORD i; RASSRV_USERDB * This = (RASSRV_USERDB*)hUserDatabase; // Make sure we were passed a valid handle if (!This) return ERROR_INVALID_PARAMETER; // We're done if there are no users if (!This->dwUserCount) return NO_ERROR; // Commit any settings as appropriate if (This->bFlushOnClose) usrFlushLocalDatabase(hUserDatabase); // Free the user cache usrFreeUserArray(This->pUserCache, This->dwUserCount); RassrvFree(This->pUserCache); // Disconnect from the user server MprAdminUserServerDisconnect (This->hServer); // Free This RassrvFree(This); return NO_ERROR; } // Flushes the data written to the database object DWORD usrFlushLocalDatabase ( IN HANDLE hUserDatabase) { RASSRV_USERDB * This = (RASSRV_USERDB*)hUserDatabase; RASSRV_USERINFO * pUser; DWORD dwErr, dwRet = NO_ERROR, dwCount, i, dwLength; dwErr = usrGetUserCount (This, &dwCount); if (dwErr != NO_ERROR) return dwErr; for (i=0; ipUserCache[i]; // Flush any dirty settings if (usrIsDirty(pUser)) { // Add the user to the local user database if it hasn't // already been done if (usrIsAddDirty(pUser)) { //For secure password bug .Net 754400 SafeDecodePasswordBuf(pUser->szPassword); dwErr = RasSrvAddUser ( pUser->pszName, (pUser->pszFullName) ? pUser->pszFullName : L"", (L'\0'!=pUser->szPassword[0]) ? pUser->szPassword : L""); SafeEncodePasswordBuf(pUser->szPassword); if (dwErr != NO_ERROR) dwRet = dwErr; // Now get the SDO handle to the user // so we can commit ras properties below. dwErr = MprAdminUserOpen ( This->hServer, pUser->pszName, &(pUser->hUser)); if (dwErr != NO_ERROR) continue; } // Flush dirty callback properties if (usrIsRasPropsDirty(pUser)) { if ((dwErr = usrCommitRasProps(This->pUserCache[i])) != NO_ERROR) dwRet = dwErr; } // Flush dirty password and full name settings if (usrIsFullNameDirty(pUser) || usrIsPasswordDirty(pUser)) { SafeDecodePasswordBuf(pUser->szPassword); RasSrvEditUser ( pUser->pszName, (usrIsFullNameDirty(pUser)) ? pUser->pszFullName : NULL, (usrIsPasswordDirty(pUser)) ? pUser->szPassword : NULL); SafeEncodePasswordBuf(pUser->szPassword); } // Reset the user as not being dirty usrClearDirty(pUser); } } // Flush the encryption setting if it has been read if (This->bEncSettingLoaded) { DWORD dwFlags; if (This->bEncrypt) dwFlags = MPR_USER_PROF_FLAG_SECURE; else dwFlags = 0; MprAdminUserWriteProfFlags (This->hServer, dwFlags); usrSetServerEnc(dwFlags); } // Flush out the purity of the system { DWORD dwPure = 0; if (This->bPure) dwPure = 0; else dwPure = 1; RassrvRegSetDw(dwPure, (const PWCHAR)pszregRasParameters, (const PWCHAR)pszregPure); } // Flush out whether dcc connections can be bypassed { DWORD dwSvrFlags = 0; RassrvRegGetDw( &dwSvrFlags, dwSvrFlags, (const PWCHAR)pszregRasParameters, (const PWCHAR)pszregServerFlags); if (This->bDccBypass) dwSvrFlags |= PPPCFG_AllowNoAuthOnDCPorts; else dwSvrFlags &= ~PPPCFG_AllowNoAuthOnDCPorts; RassrvRegSetDw( dwSvrFlags, (const PWCHAR)pszregRasParameters, (const PWCHAR)pszregServerFlags); } return dwRet; } // Rolls back the local user database so that no // changes will be committed when Flush is called. DWORD usrRollbackLocalDatabase ( IN HANDLE hUserDatabase) { DWORD i, dwIndex, dwErr; BOOL bCommit; RASSRV_USERDB * This = (RASSRV_USERDB*)hUserDatabase; if (!This) return ERROR_INVALID_PARAMETER; if (!This->dwUserCount) return NO_ERROR; // Go through the database, marking each user as not dirty for (i = 0; i < This->dwUserCount; i++) usrClearDirty(This->pUserCache[i]); This->bFlushOnClose = FALSE; return NO_ERROR; } // // Determines whether all users are required to encrypt // their data and passwords. // DWORD usrGetEncryption ( IN HANDLE hUserDatabase, OUT PBOOL pbEncrypted) { RASSRV_USERDB * This = (RASSRV_USERDB*)hUserDatabase; if (!This) { return ERROR_INVALID_PARAMETER; } // Load in the encryption setting usrLoadEncryptionSetting(This); *pbEncrypted = This->bEncrypt; return NO_ERROR; } // Gets user encryption setting DWORD usrSetEncryption ( IN HANDLE hUserDatabase, IN BOOL bEncrypt) { RASSRV_USERDB * This = (RASSRV_USERDB*)hUserDatabase; if (!This) { return ERROR_INVALID_PARAMETER; } // Load in the encryption setting usrLoadEncryptionSetting(This); This->bEncrypt = bEncrypt; return NO_ERROR; } // Returns whether dcc connections are allowed to // bypass authentication DWORD usrGetDccBypass ( IN HANDLE hUserDatabase, OUT PBOOL pbBypass) { RASSRV_USERDB * This = (RASSRV_USERDB*)hUserDatabase; if (!This) return ERROR_INVALID_PARAMETER; *pbBypass = This->bDccBypass; return NO_ERROR; } // Sets whether dcc connections are allowed to // bypass authentication DWORD usrSetDccBypass ( IN HANDLE hUserDatabase, IN BOOL bBypass) { RASSRV_USERDB * This = (RASSRV_USERDB*)hUserDatabase; if (!This) return ERROR_INVALID_PARAMETER; This->bDccBypass = bBypass; return NO_ERROR; } // Reports whether the user database is pure. (i.e. nobody has // gone into MMC and messed with it). DWORD usrIsDatabasePure ( IN HANDLE hUserDatabase, OUT PBOOL pbPure) { RASSRV_USERDB * This = (RASSRV_USERDB*)hUserDatabase; if (!This) return ERROR_INVALID_PARAMETER; *pbPure = This->bPure; return NO_ERROR; } // Marks the user database's purity DWORD usrSetDatabasePure( IN HANDLE hUserDatabase, IN BOOL bPure) { RASSRV_USERDB * This = (RASSRV_USERDB*)hUserDatabase; if (!This) return ERROR_INVALID_PARAMETER; This->bPure = bPure; return NO_ERROR; } // Returns the number of users cached in this database DWORD usrGetUserCount ( IN HANDLE hUserDatabase, OUT LPDWORD lpdwCount) { RASSRV_USERDB * This = (RASSRV_USERDB*)hUserDatabase; if (!This || !lpdwCount) return ERROR_INVALID_PARAMETER; *lpdwCount = This->dwUserCount; return NO_ERROR; } // Adds a user to the given database. This user will not be // added to the system's local user database until this database // object is flushed (and as long as Rollback is not called on // this database object) // // On success, an optional handle to the user is returned // DWORD usrAddUser ( IN HANDLE hUserDatabase, IN PWCHAR pszName, OUT OPTIONAL HANDLE * phUser) { RASSRV_USERDB * This = (RASSRV_USERDB*)hUserDatabase; RASSRV_USERINFO * pUser; DWORD dwErr, dwLength; // Validate the parameters if (!This || !pszName) return ERROR_INVALID_PARAMETER; // If the user already exists, don't add him if (usrUserExists(This, pszName)) return ERROR_ALREADY_EXISTS; // Resize the cache to accomodate if neccessary if (This->dwUserCount + 1 >= This->dwCacheSize) { dwErr = usrResizeCache(This, This->dwCacheSize + USR_ARRAY_GROW_SIZE); if (dwErr != NO_ERROR) return dwErr; } // Allocate the new user control block if ((pUser = RassrvAlloc(sizeof(RASSRV_USERINFO), TRUE)) == NULL) return ERROR_NOT_ENOUGH_MEMORY; // Allocate space for the name dwLength = wcslen(pszName); pUser->pszName = RassrvAlloc((dwLength + 1) * sizeof(WCHAR), FALSE); if (pUser->pszName == NULL) return ERROR_NOT_ENOUGH_MEMORY; // Copy the name wcscpy(pUser->pszName, pszName); // Enable the user for dialin by default usrEnableDialin ((HANDLE)pUser, TRUE); // Dirty the user usrDirtyAdd(pUser); usrDirtyRasProps(pUser); // Put the user in the array and re-sort it This->pUserCache[This->dwUserCount++] = pUser; usrResortCache(This); // Return the handle if (phUser) *phUser = (HANDLE)pUser; //Need this zero memory to tell if a password is set in the future. RtlSecureZeroMemory(pUser->szPassword,sizeof(pUser->szPassword)); //Other place will always assume the password is encrypted //So encrypt even the empty password SafeEncodePasswordBuf(pUser->szPassword); return NO_ERROR; } // Gives the count of users stored in the user database object // Deletes the given user DWORD usrDeleteUser ( IN HANDLE hUserDatabase, IN DWORD dwIndex) { RASSRV_USERDB * This = (RASSRV_USERDB*)hUserDatabase; RASSRV_USERINFO * pUser; DWORD dwErr, dwMoveElemCount; // Validate the parameters if (!This) return ERROR_INVALID_PARAMETER; // Bounds Check if (!usrBoundsCheck(This, dwIndex)) return ERROR_INVALID_INDEX; // Get a reference to the user in question and remove him // from the cache pUser = This->pUserCache[dwIndex]; //Need this to clear password area and any memory allocated(if CryptProtectData() is used) // SafeWipePasswordBuf(pUser->szPassword); // Attempt to delete the user from the system if ((dwErr = RasSrvDeleteUser(pUser->pszName)) != NO_ERROR) return dwErr; // Remove the user from the cache This->pUserCache[dwIndex] = NULL; // Pull down every thing in the cache so that there are no holes dwMoveElemCount = This->dwUserCount - dwIndex; if (dwMoveElemCount) { MoveMemory(&(This->pUserCache[dwIndex]), &(This->pUserCache[dwIndex + 1]), dwMoveElemCount * sizeof(RASSRV_USERINFO*)); } // Decrement the number of users This->dwUserCount--; // Cleanup the user usrFreeUserArray(&pUser, 1); return NO_ERROR; } // Gives a handle to the user at the given index DWORD usrGetUserHandle ( IN HANDLE hUserDatabase, IN DWORD dwIndex, OUT HANDLE * hUser) { RASSRV_USERDB * This = (RASSRV_USERDB*)hUserDatabase; if (!This || !hUser) return ERROR_INVALID_PARAMETER; if (!usrBoundsCheck(This, dwIndex)) return ERROR_INVALID_INDEX; *hUser = (HANDLE)(This->pUserCache[dwIndex]); return NO_ERROR; } // Gets a pointer to the name of the user (do not modify this) DWORD usrGetName ( IN HANDLE hUser, OUT PWCHAR* pszName) { RASSRV_USERINFO* pRassrvUser = (RASSRV_USERINFO*)hUser; if (!pRassrvUser || !pszName) return ERROR_INVALID_PARAMETER; *pszName = pRassrvUser->pszName; return NO_ERROR; } // Fills the given buffer with a friendly display name // (in the form username (fullname)) //*lpdwBuffSize is the number of Characters, NOT including the ending NULL DWORD usrGetDisplayName ( IN HANDLE hUser, IN PWCHAR pszBuffer, IN OUT LPDWORD lpdwBufSize) { RASSRV_USERINFO * pRassrvUser = (RASSRV_USERINFO*)hUser; NET_API_STATUS nStatus; DWORD dwUserNameLength, dwFullLength, dwSizeRequired; WCHAR pszTemp[IC_USERFULLNAME]; // For whistler bug 39081 gangz DWORD dwErr = NO_ERROR; // Sanity check the params if (!pRassrvUser || !pszBuffer || !lpdwBufSize) { return ERROR_INVALID_PARAMETER; } do { // Get the full name of the user // For whistler bug 39081 gangz // This is size in bytes dwFullLength = sizeof(pszTemp)/sizeof(pszTemp[0]); dwErr = usrGetFullName(hUser, pszTemp, &dwFullLength); if (dwErr != NO_ERROR) { break; } // Make sure the buffer is big enough dwUserNameLength = wcslen(pRassrvUser->pszName); dwSizeRequired = dwUserNameLength + dwFullLength + ((dwFullLength) ? 3 : 0); if (*lpdwBufSize < dwSizeRequired) { dwErr = ERROR_INSUFFICIENT_BUFFER; break; } if (dwFullLength) { wsprintfW( pszBuffer, L"%s (%s)", pRassrvUser->pszName, pszTemp); } else { wcscpy(pszBuffer, pRassrvUser->pszName); } } while (FALSE); // Cleanup { // The number of characters required, NOT including ending NULL *lpdwBufSize = dwSizeRequired; } return dwErr; } // Fills the given buffer with a friendly display name // (in the form username (fullname)) // *lpdwBufSize is the number of characters, NOT including ending NULL DWORD usrGetFullName ( IN HANDLE hUser, IN PWCHAR pszBuffer, IN OUT LPDWORD lpdwBufSize) { RASSRV_USERINFO * pRassrvUser = (RASSRV_USERINFO*)hUser; NET_API_STATUS nStatus; USER_INFO_2 * pUserInfo = NULL; DWORD dwLength; PWCHAR pszFullName; DWORD dwErr = NO_ERROR; // Sanity check the params if (!pRassrvUser || !pszBuffer || !lpdwBufSize) return ERROR_INVALID_PARAMETER; // If the full name is already loaded, return it if (pRassrvUser->pszFullName) pszFullName = pRassrvUser->pszFullName; // or if this is a new user, get the name from memory else if (usrIsAddDirty(pRassrvUser)) { pszFullName = (pRassrvUser->pszFullName) ? pRassrvUser->pszFullName : L""; } // Load the full name of the user else { nStatus = NetUserGetInfo( NULL, pRassrvUser->pszName, 2, (LPBYTE*)&pUserInfo); if (nStatus != NERR_Success) { DbgOutputTrace ( "usrGetFullName: %x returned from NetUserGetInfo for %S", nStatus, pRassrvUser->pszName); return nStatus; } pszFullName = (PWCHAR)pUserInfo->usri2_full_name; } do { // Make sure the length is ok dwLength = wcslen(pszFullName); // Assign the full name here if it hasn't already been done if (dwLength && !pRassrvUser->pszFullName) { DWORD dwSize = dwLength * sizeof(WCHAR) + sizeof(WCHAR); pRassrvUser->pszFullName = RassrvAlloc(dwSize, FALSE); if (pRassrvUser->pszFullName) { wcscpy(pRassrvUser->pszFullName, pszFullName); } } // Check the size NOT including ending NULL if (*lpdwBufSize < dwLength ) { dwErr = ERROR_INSUFFICIENT_BUFFER; break; } // Copy in the full name wcscpy(pszBuffer, pszFullName); } while (FALSE); // Cleanup { // report the size in number of characters ( NOT include ending NULL) *lpdwBufSize = dwLength; if (pUserInfo) { NetApiBufferFree((LPBYTE)pUserInfo); } } return dwErr; } // Commits the full name of a user DWORD usrSetFullName ( IN HANDLE hUser, IN PWCHAR pszFullName) { RASSRV_USERINFO* pRassrvUser = (RASSRV_USERINFO*)hUser; DWORD dwLength; if (!pRassrvUser || !pszFullName) return ERROR_INVALID_PARAMETER; // If this is not a new name, don't do anything if (pRassrvUser->pszFullName) { if (wcscmp(pRassrvUser->pszFullName, pszFullName) == 0) return NO_ERROR; RassrvFree(pRassrvUser->pszFullName); } // Allocate a new one dwLength = wcslen(pszFullName); pRassrvUser->pszFullName = RassrvAlloc(dwLength * sizeof(WCHAR) + sizeof(WCHAR), FALSE); if (!pRassrvUser->pszFullName) return ERROR_NOT_ENOUGH_MEMORY; // Copy it over wcscpy(pRassrvUser->pszFullName, pszFullName); // Mark it dirty -- a newly added user has his/her full name commited // whenever it exists automatically if (!usrIsAddDirty(pRassrvUser)) usrDirtyFullname(pRassrvUser); return NO_ERROR; } // Commits the password of a user //pszNewPassword is not encoded DWORD usrSetPassword ( IN HANDLE hUser, IN PWCHAR pszNewPassword) { RASSRV_USERINFO* pRassrvUser = (RASSRV_USERINFO*)hUser; DWORD dwLength; if (!pRassrvUser || !pszNewPassword) return ERROR_INVALID_PARAMETER; // Cleanup the old password if it exists SafeWipePasswordBuf(pRassrvUser->szPassword); // Allocate a new one dwLength = wcslen(pszNewPassword); // Copy it over lstrcpynW(pRassrvUser->szPassword, pszNewPassword, sizeof(pRassrvUser->szPassword)/sizeof(pRassrvUser->szPassword[0]) ); // Encrypt it SafeEncodePasswordBuf( pRassrvUser->szPassword ); // Mark it dirty -- a newly added user has his/her full name commited // whenever it exists automatically if (!usrIsAddDirty(pRassrvUser)) usrDirtyPassword(pRassrvUser); return NO_ERROR; } // Determines whether users have callback/dialin priveleges. DWORD usrGetDialin ( IN HANDLE hUser, OUT BOOL* bEnabled) { RASSRV_USERINFO* pRassrvUser = (RASSRV_USERINFO*)hUser; DWORD dwErr; RAS_USER_0 UserInfo; if (!pRassrvUser || !bEnabled) return ERROR_INVALID_PARAMETER; // Get the user info *bEnabled = (pRassrvUser->bfPrivilege & RASPRIV_DialinPrivilege); return NO_ERROR; } // Determines which if any callback priveleges are granted to a given user. // Either (or both) of bAdminOnly and bUserSettable can be null DWORD usrGetCallback ( IN HANDLE hUser, OUT BOOL* bAdminOnly, OUT BOOL* bUserSettable) { RASSRV_USERINFO* pRassrvUser = (RASSRV_USERINFO*)hUser; DWORD dwErr; if (!pRassrvUser || !bAdminOnly || !bUserSettable) return ERROR_INVALID_PARAMETER; // Return whether we have callback privelege if (bAdminOnly) { *bAdminOnly = (pRassrvUser->bfPrivilege & RASPRIV_AdminSetCallback); } if (bUserSettable) { *bUserSettable = (pRassrvUser->bfPrivilege & RASPRIV_CallerSetCallback); } return NO_ERROR; } // Enable/disable dialin privelege. DWORD usrEnableDialin ( IN HANDLE hUser, IN BOOL bEnable) { RASSRV_USERINFO* pRassrvUser = (RASSRV_USERINFO*)hUser; DWORD dwErr = NO_ERROR; BOOL bIsEnabled; if (!pRassrvUser) return ERROR_INVALID_PARAMETER; // If the dialin privelege is already set as requested return success bIsEnabled = pRassrvUser->bfPrivilege & RASPRIV_DialinPrivilege; if ((!!bIsEnabled) == (!!bEnable)) return NO_ERROR; // Otherwise reset the privelege if (bEnable) pRassrvUser->bfPrivilege |= RASPRIV_DialinPrivilege; else pRassrvUser->bfPrivilege &= ~RASPRIV_DialinPrivilege; // Dirty the user (cause him/her to be flushed at apply time) usrDirtyRasProps(pRassrvUser); return dwErr; } // The flags are evaluated in the following order with whichever condition // being satisfied fist defining the behavior of the function. // bNone == TRUE => Callback is disabled for the user // bCaller == TRUE => Callback is set to caller-settable // bAdmin == TRUE => Callback is set to a predefine callback number set // All 3 are FALSE => No op DWORD usrEnableCallback ( IN HANDLE hUser, IN BOOL bNone, IN BOOL bCaller, IN BOOL bAdmin) { RASSRV_USERINFO* pRassrvUser = (RASSRV_USERINFO*)hUser; DWORD dwErr = NO_ERROR; BOOL bIsEnabled; if (!pRassrvUser) return ERROR_INVALID_PARAMETER; if (bNone) { pRassrvUser->bfPrivilege |= RASPRIV_NoCallback; pRassrvUser->bfPrivilege &= ~RASPRIV_CallerSetCallback; pRassrvUser->bfPrivilege &= ~RASPRIV_AdminSetCallback; } else if (bCaller) { pRassrvUser->bfPrivilege &= ~RASPRIV_NoCallback; pRassrvUser->bfPrivilege |= RASPRIV_CallerSetCallback; pRassrvUser->bfPrivilege &= ~RASPRIV_AdminSetCallback; } else if (bAdmin) { pRassrvUser->bfPrivilege &= ~RASPRIV_NoCallback; pRassrvUser->bfPrivilege &= ~RASPRIV_CallerSetCallback; pRassrvUser->bfPrivilege |= RASPRIV_AdminSetCallback; } else return NO_ERROR; // Dirty the user (cause him/her to be flushed at apply time) usrDirtyRasProps(pRassrvUser); return dwErr; } // Retreives a pointer to the callback number of the given user DWORD usrGetCallbackNumber( IN HANDLE hUser, OUT PWCHAR * lpzNumber) { RASSRV_USERINFO* pRassrvUser = (RASSRV_USERINFO*)hUser; if (!pRassrvUser || !lpzNumber) return ERROR_INVALID_PARAMETER; // Return the pointer to the callback number *lpzNumber = pRassrvUser->wszPhoneNumber; return NO_ERROR; } // Sets the callback number of the given user. If lpzNumber is NULL, // an empty phone number is copied. DWORD usrSetCallbackNumber( IN HANDLE hUser, IN PWCHAR lpzNumber) { RASSRV_USERINFO* pRassrvUser = (RASSRV_USERINFO*)hUser; DWORD dwErr = NO_ERROR; if (!pRassrvUser) return ERROR_INVALID_PARAMETER; // Modify the phone number appropriately if (!lpzNumber) wcscpy(pRassrvUser->wszPhoneNumber, L""); else { lstrcpynW(pRassrvUser->wszPhoneNumber, lpzNumber, MAX_PHONE_NUMBER_LEN); pRassrvUser->wszPhoneNumber[MAX_PHONE_NUMBER_LEN] = (WCHAR)0; } // Dirty the user (cause him/her to be flushed at apply time) usrDirtyRasProps(pRassrvUser); return dwErr; }