//+----------------------------------------------------------------------------
//
// File:     getpbk.cpp
//
// Module:   Common Code
//
// Synopsis: Implements the function GetPhoneBookPath.
//
// Copyright (c) 1999 Microsoft Corporation
//
// Author:   quintinb    Created Heaser   08/19/99
//
//+----------------------------------------------------------------------------


//+----------------------------------------------------------------------------
//
// Function:  AllocateSecurityDescriptorAllowAccessToWorld
//
// Synopsis:  This function allocates a security descriptor for all users.
//            This function was taken directly from RAS when they create their
//            phonebook. This has to be before GetPhoneBookPath otherwise it 
//            causes compile errors in other components since we don't have a
//            function prototype anywhere and cmcfg just includes this (getpbk.cpp)
//            file. This function is also in cmdial\ras.cpp
//
// Arguments: PSECURITY_DESCRIPTOR *ppSd - Pointer to a pointer to the SD struct
//
// Returns:   DWORD - returns ERROR_SUCCESS if successfull
//
// History:   06/27/2001    tomkel  Taken from RAS ui\common\pbk\file.c
//
//+----------------------------------------------------------------------------
#define SIZE_ALIGNED_FOR_TYPE(_size, _type) \
    (((_size) + sizeof(_type)-1) & ~(sizeof(_type)-1))

DWORD AllocateSecurityDescriptorAllowAccessToWorld(PSECURITY_DESCRIPTOR *ppSd)
{
    PSECURITY_DESCRIPTOR    pSd;
    PSID                    pSid;
    PACL                    pDacl;
    DWORD                   dwErr = ERROR_SUCCESS;
    DWORD                   dwAlignSdSize;
    DWORD                   dwAlignDaclSize;
    DWORD                   dwSidSize;
    PVOID                   pvBuffer;
    DWORD                   dwAcls = 0;

    // Here is the buffer we are building.
    //
    //   |<- a ->|<- b ->|<- c ->|
    //   +-------+--------+------+
    //   |      p|      p|       |
    //   | SD   a| DACL a| SID   |
    //   |      d|      d|       |
    //   +-------+-------+-------+
    //   ^       ^       ^
    //   |       |       |
    //   |       |       +--pSid
    //   |       |
    //   |       +--pDacl
    //   |
    //   +--pSd (this is returned via *ppSd)
    //
    //   pad is so that pDacl and pSid are aligned properly.
    //
    //   a = dwAlignSdSize
    //   b = dwAlignDaclSize
    //   c = dwSidSize
    //

    if (NULL == ppSd)
    {
        return ERROR_INVALID_PARAMETER;
    }

    // Initialize output parameter.
    //
    *ppSd = NULL;

    // Compute the size of the SID.  The SID is the well-known SID for World
    // (S-1-1-0).
    //
    dwSidSize = GetSidLengthRequired(1);

    // Compute the size of the DACL.  It has an inherent copy of SID within
    // it so add enough room for it.  It also must sized properly so that
    // a pointer to a SID structure can come after it.  Hence, we use
    // SIZE_ALIGNED_FOR_TYPE.
    //
    dwAlignDaclSize = SIZE_ALIGNED_FOR_TYPE(
                        sizeof(ACCESS_ALLOWED_ACE) + sizeof(ACL) + dwSidSize,
                        PSID);

    // Compute the size of the SD.  It must be sized propertly so that a
    // pointer to a DACL structure can come after it.  Hence, we use
    // SIZE_ALIGNED_FOR_TYPE.
    //
    dwAlignSdSize   = SIZE_ALIGNED_FOR_TYPE(
                        sizeof(SECURITY_DESCRIPTOR),
                        PACL);

    // Allocate the buffer big enough for all.
    //
    dwErr = ERROR_OUTOFMEMORY;
    pvBuffer = CmMalloc(dwSidSize + dwAlignDaclSize + dwAlignSdSize);
    if (pvBuffer)
    {
        SID_IDENTIFIER_AUTHORITY SidIdentifierWorldAuth
                                    = SECURITY_WORLD_SID_AUTHORITY;
        PULONG  pSubAuthority;

        dwErr = NOERROR;

        // Setup the pointers into the buffer.
        //
        pSd   = pvBuffer;
        pDacl = (PACL)((PBYTE)pvBuffer + dwAlignSdSize);
        pSid  = (PSID)((PBYTE)pDacl + dwAlignDaclSize);

        // Initialize pSid as S-1-1-0.
        //
        if (!InitializeSid(
                pSid,
                &SidIdentifierWorldAuth,
                1))  // 1 sub-authority
        {
            dwErr = GetLastError();
            goto finish;
        }

        pSubAuthority = GetSidSubAuthority(pSid, 0);
        *pSubAuthority = SECURITY_WORLD_RID;

        // Initialize pDacl.
        //
        if (!InitializeAcl(
                pDacl,
                dwAlignDaclSize,
                ACL_REVISION))
        {
            dwErr = GetLastError();
            goto finish;
        }

        dwAcls = SPECIFIC_RIGHTS_ALL | STANDARD_RIGHTS_ALL;

        dwAcls &= ~(WRITE_DAC | WRITE_OWNER);
        
        if(!AddAccessAllowedAce(
                pDacl,
                ACL_REVISION,
                dwAcls,
                pSid))
        {
            dwErr = GetLastError();
            goto finish;
        }

        // Initialize pSd.
        //
        if (!InitializeSecurityDescriptor(
                pSd,
                SECURITY_DESCRIPTOR_REVISION))
        {
            dwErr = GetLastError();
            goto finish;
        }

        // Set pSd to use pDacl.
        //
        if (!SetSecurityDescriptorDacl(
                pSd,
                TRUE,
                pDacl,
                FALSE))
        {
            dwErr = GetLastError();
            goto finish;
        }

        // Set the owner for pSd.
        //
        if (!SetSecurityDescriptorOwner(
                pSd,
                NULL,
                TRUE))
        {
            dwErr = GetLastError();
            goto finish;
        }

        // Set the group for pSd.
        //
        if (!SetSecurityDescriptorGroup(
                pSd,
                NULL,
                FALSE))
        {
            dwErr = GetLastError();
            goto finish;
        }

finish:
        if (!dwErr)
        {
            *ppSd = pSd;
        }
        else
        {
            CmFree(pvBuffer);
        }
    }

    return dwErr;
}


//+----------------------------------------------------------------------------
//
// Function:  GetPhoneBookPath
//
// Synopsis:  This function will return the proper path to the phonebook.  If
//            used on a legacy platform this is NULL.  On NT5, the function
//            depends on the proper Install Directory being inputted so that
//            the function can use this as a base to determine the phonebook path.
//            If the inputted pointer to a string buffer is filled with a path,
//            then the directory path will be created as will the pbk file itself.
//            The caller should always call CmFree on the pointer passed into this
//            API when done with the path, because it will either free the memory 
//            or do nothing (NULL case).
//
// Arguments: LPCTSTR pszInstallDir - path to the CM profile dir
//            LPTSTR* ppszPhoneBook - pointer to accept a newly allocated and filled pbk string
//            BOOL fAllUser         - TRUE if this an All-User profile
//
// Returns:   BOOL - returns TRUE if successful
//
// History:   quintinb Created    11/12/98
//            tomkel   06/28/2001   Changed the ACLs when the phonebook gets 
//                                  createdfor an All-User profile
//
//+----------------------------------------------------------------------------
BOOL GetPhoneBookPath(LPCTSTR pszInstallDir, LPTSTR* ppszPhonebook, BOOL fAllUser)
{

    if (NULL == ppszPhonebook)
    {
        CMASSERTMSG(FALSE, TEXT("GetPhoneBookPath -- Invalid Parameter"));
        return FALSE;
    }

    CPlatform plat;

    if (plat.IsAtLeastNT5())
    {
        if ((NULL == pszInstallDir) || (TEXT('\0') == pszInstallDir[0]))
        {
            CMASSERTMSG(FALSE, TEXT("GetPhoneBookPath -- Invalid Install Dir parameter."));
            return FALSE;
        }

        //
        //  Now Create the path to the phonebook.
        //
        LPTSTR pszPhonebook;
        TCHAR szInstallDir[MAX_PATH+1];
        ZeroMemory(szInstallDir, CELEMS(szInstallDir));

        if (TEXT('\\') == pszInstallDir[lstrlen(pszInstallDir) - 1])
        {
            //
            //  Then the path ends in a backslash.  Thus we won't properly
            //  remove CM from the path.  Remove the backslash.
            //
            
            lstrcpyn(szInstallDir, pszInstallDir, lstrlen(pszInstallDir));
        }
        else
        {
            lstrcpy(szInstallDir, pszInstallDir);
        }

        CFileNameParts InstallDirPath(szInstallDir);

        pszPhonebook = (LPTSTR)CmMalloc(lstrlen(InstallDirPath.m_Drive) + 
                                        lstrlen(InstallDirPath.m_Dir) + 
                                        lstrlen(c_pszPbk) + lstrlen(c_pszRasPhonePbk) + 1);

        if (NULL != pszPhonebook)
        {
            wsprintf(pszPhonebook, TEXT("%s%s%s"), InstallDirPath.m_Drive, 
                InstallDirPath.m_Dir, c_pszPbk);

            //
            //  Use CreateLayerDirectory to recursively create the directory structure as
            //  necessary (will create all the directories in a full path if necessary).
            //

            MYVERIFY(FALSE != CreateLayerDirectory(pszPhonebook));

            MYVERIFY(NULL != lstrcat(pszPhonebook, c_pszRasPhonePbk));
            
            HANDLE hPbk = INVALID_HANDLE_VALUE;

            SECURITY_ATTRIBUTES sa = {0};
            PSECURITY_ATTRIBUTES pSA = NULL;
            PSECURITY_DESCRIPTOR pSd = NULL;

            if (fAllUser)
            {
                //
                // For an All-User profile be sure to create it with a 
                // security descriptor that  allows it to be read by any authenticated 
                // user.  If we don't it may  prevent other users from being able to 
                // read it. We didn't want to change the old behavior downlevel so this 
                // fix is just for Whistler+.
                //
                DWORD dwErr = AllocateSecurityDescriptorAllowAccessToWorld(&pSd);
                if ((ERROR_SUCCESS == dwErr) && pSd)
                {
                    sa.nLength = sizeof(SECURITY_ATTRIBUTES);
                    sa.lpSecurityDescriptor = pSd;
                    sa.bInheritHandle = TRUE;
                    pSA = &sa;
                }
            }

            hPbk = CreateFile(pszPhonebook, GENERIC_WRITE | GENERIC_READ, FILE_SHARE_READ, pSA, CREATE_NEW,
                              FILE_ATTRIBUTE_NORMAL, NULL);

            CmFree(pSd);

            if (hPbk != INVALID_HANDLE_VALUE)
            {
                MYVERIFY(0 != CloseHandle(hPbk));
            }

            *ppszPhonebook = pszPhonebook;
        }
        else
        {
            CMASSERTMSG(FALSE, TEXT("CmMalloc returned NULL"));
            return FALSE;
        }    
    }
    else
    {
        *ppszPhonebook = NULL;
    }

    return TRUE;
}