/*
 * Copyright Likewise Software
 * All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or (at
 * your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * for more details.  You should have received a copy of the GNU General
 * Public License along with this program.  If not, see
 * <http://www.gnu.org/licenses/>.
 *
 * LIKEWISE SOFTWARE MAKES THIS SOFTWARE AVAILABLE UNDER OTHER LICENSING
 * TERMS AS WELL.  IF YOU HAVE ENTERED INTO A SEPARATE LICENSE AGREEMENT
 * WITH LIKEWISE SOFTWARE, THEN YOU MAY ELECT TO USE THE SOFTWARE UNDER THE
 * TERMS OF THAT SOFTWARE LICENSE AGREEMENT INSTEAD OF THE TERMS OF THE GNU
 * GENERAL PUBLIC LICENSE, NOTWITHSTANDING THE ABOVE NOTICE.  IF YOU
 * HAVE QUESTIONS, OR WISH TO REQUEST A COPY OF THE ALTERNATE LICENSING
 * TERMS OFFERED BY LIKEWISE SOFTWARE, PLEASE CONTACT LIKEWISE SOFTWARE AT
 * license@likewisesoftware.com
 */

/*
 * Module Name:
 *
 *        main.c
 *
 * Abstract:
 *
 *        Utility for modifying the security descriptor information
 *        on a locally residing file.
 *
 * Authors: Justin T. Gibbs (justing@spectralogic.com)
 *
 */

#include "includes.h"

static
VOID
Usage(
    VOID
    )
{
    printf("Usage: lwio-setfileinfo [-e] <absolute_path>\n");
    printf("\n");
    printf("    where:\n");
    printf("    -e:               grants \"everyone\" full access\n");
    printf("    <absolute_path>:  Specifes the share root\n");

    exit(STATUS_INVALID_PARAMETER);
}

static
NTSTATUS
BuildDefaultShareSD(
    PSECURITY_DESCRIPTOR_RELATIVE* ppSecDesc,
    PULONG pulRelSecDescLen,
    ACCESS_MASK extraWorldAccess
    )
{
    NTSTATUS ntStatus = STATUS_SUCCESS;
    PSECURITY_DESCRIPTOR_RELATIVE pRelSecDesc = NULL;
    ULONG ulRelSecDescLen = 0;
    PSECURITY_DESCRIPTOR_ABSOLUTE pAbsSecDesc = NULL;
    DWORD dwAceCount = 0;
    PSID pOwnerSid = NULL;
    PSID pGroupSid = NULL;
    PACL pDacl = NULL;
    DWORD dwSizeDacl = 0;
    union
    {
        SID sid;
        BYTE buffer[SID_MAX_SIZE];
    } localSystemSid;
    union
    {
        SID sid;
        BYTE buffer[SID_MAX_SIZE];
    } administratorsSid;
    union
    {
        SID sid;
        BYTE buffer[SID_MAX_SIZE];
    } worldSid;
    ULONG ulLocalSystemSidSize = sizeof(localSystemSid.buffer);
    ULONG ulAdministratorsSidSize = sizeof(administratorsSid.buffer);
    ULONG ulWorldSidSize = sizeof(worldSid.buffer);
    ACCESS_MASK worldAccessMask = 0;
    ULONG ulAceFlags = OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE;

    /* Build the new Absolute Security Descriptor */

    ntStatus = RTL_ALLOCATE(
                   &pAbsSecDesc,
                   VOID,
                   SECURITY_DESCRIPTOR_ABSOLUTE_MIN_SIZE);
    BAIL_ON_NT_STATUS(ntStatus);

    ntStatus = RtlCreateSecurityDescriptorAbsolute(
                  pAbsSecDesc,
                  SECURITY_DESCRIPTOR_REVISION);
    BAIL_ON_NT_STATUS(ntStatus);

    /* Create some SIDs */

    ntStatus = RtlCreateWellKnownSid(
                   WinLocalSystemSid,
                   NULL,
                   (PSID)localSystemSid.buffer,
                   &ulLocalSystemSidSize);
    BAIL_ON_NT_STATUS(ntStatus);

    ntStatus = RtlCreateWellKnownSid(
                   WinBuiltinAdministratorsSid,
                   NULL,
                   (PSID)administratorsSid.buffer,
                   &ulAdministratorsSidSize);
    BAIL_ON_NT_STATUS(ntStatus);

    ntStatus = RtlCreateWellKnownSid(
                    WinWorldSid,
                    NULL,
                    (PSID)worldSid.buffer,
                    &ulWorldSidSize);
    BAIL_ON_NT_STATUS(ntStatus);

    /* Owner: Local System */

    ntStatus = RtlDuplicateSid(&pOwnerSid, &localSystemSid.sid);
    BAIL_ON_NT_STATUS(ntStatus);

    ntStatus = RtlSetOwnerSecurityDescriptor(
                   pAbsSecDesc,
                   pOwnerSid,
                   FALSE);
    BAIL_ON_NT_STATUS(ntStatus);

    /* Group: Administrators */

    ntStatus = RtlDuplicateSid(&pGroupSid, &administratorsSid.sid);
    BAIL_ON_NT_STATUS(ntStatus);

    ntStatus = RtlSetGroupSecurityDescriptor(
                   pAbsSecDesc,
                   pGroupSid,
                   FALSE);
    BAIL_ON_NT_STATUS(ntStatus);

    /* DACL:
       LocalSystem    - (Full Control)
       Administrators - (Full Control)
       Everyone       - (Read && Execute && List Directory Contents)
     */

    dwAceCount = 3;

    dwSizeDacl = ACL_HEADER_SIZE +
        dwAceCount * sizeof(ACCESS_ALLOWED_ACE) +
        RtlLengthSid(&localSystemSid.sid) +
        RtlLengthSid(&administratorsSid.sid) +
        RtlLengthSid(&worldSid.sid) -
        dwAceCount * sizeof(ULONG);

    ntStatus= RTL_ALLOCATE(&pDacl, VOID, dwSizeDacl);
    BAIL_ON_NT_STATUS(ntStatus);

    ntStatus = RtlCreateAcl(pDacl, dwSizeDacl, ACL_REVISION);
    BAIL_ON_NT_STATUS(ntStatus);

    ntStatus = RtlAddAccessAllowedAceEx(
                  pDacl,
                  ACL_REVISION,
                  ulAceFlags,
                  FILE_ALL_ACCESS,
                  &localSystemSid.sid);
    BAIL_ON_NT_STATUS(ntStatus);

    ntStatus = RtlAddAccessAllowedAceEx(
                  pDacl,
                  ACL_REVISION,
                  ulAceFlags,
                  FILE_ALL_ACCESS,
                  &administratorsSid.sid);
    BAIL_ON_NT_STATUS(ntStatus);

    worldAccessMask = FILE_GENERIC_READ | FILE_GENERIC_EXECUTE | extraWorldAccess;

    ntStatus = RtlAddAccessAllowedAceEx(
                  pDacl,
                  ACL_REVISION,
                  ulAceFlags,
                  worldAccessMask,
                  &worldSid.sid);
    BAIL_ON_NT_STATUS(ntStatus);

    ntStatus = RtlSetDaclSecurityDescriptor(
                  pAbsSecDesc,
                  TRUE,
                  pDacl,
                  FALSE);
    BAIL_ON_NT_STATUS(ntStatus);

    /* Create the SelfRelative SD */

    ntStatus = RtlAbsoluteToSelfRelativeSD(
                   pAbsSecDesc,
                   NULL,
                   &ulRelSecDescLen);
    if (ntStatus == STATUS_BUFFER_TOO_SMALL)
    {
        ntStatus = RTL_ALLOCATE((PVOID*)&pRelSecDesc, VOID, ulRelSecDescLen);
        BAIL_ON_NT_STATUS(ntStatus);

        ntStatus = RtlAbsoluteToSelfRelativeSD(
                       pAbsSecDesc,
                       pRelSecDesc,
                       &ulRelSecDescLen);
    }
    BAIL_ON_NT_STATUS(ntStatus);

    *ppSecDesc = pRelSecDesc;
    *pulRelSecDescLen = ulRelSecDescLen;

cleanup:

    RTL_FREE(&pAbsSecDesc);
    RTL_FREE(&pOwnerSid);
    RTL_FREE(&pGroupSid);
    RTL_FREE(&pDacl);

    return ntStatus;

error:

    *ppSecDesc = NULL;

    if (pRelSecDesc)
    {
        RTL_FREE(&pRelSecDesc);
    }

    goto cleanup;
}

int
main(
    int argc,
    char* argv[]
    )
{
    NTSTATUS status = STATUS_SUCCESS;
    int EE = 0;
    IO_FILE_HANDLE fileHandle = NULL;
    IO_STATUS_BLOCK ioStatusBlock = { 0 };
    IO_FILE_NAME fileName = { 0 };
    PSECURITY_DESCRIPTOR_RELATIVE pRelSecDesc = NULL;
    ULONG ulRelSecDescLen = 0;
    PCSTR pszPath = NULL;
    PCSTR pszFsRoot = "//pvfs";
    PSTR pszFsPath = NULL;
    ACCESS_MASK extraAccess = 0;
    SECURITY_INFORMATION secInfo = (OWNER_SECURITY_INFORMATION|
                                    GROUP_SECURITY_INFORMATION|
                                    DACL_SECURITY_INFORMATION);

    if (argc == 3)
    {
	if (strncmp(argv[1], "-e", 2) != 0)
    	{
            Usage();
	}
	extraAccess = FILE_ALL_ACCESS;
    	pszPath = argv[2];
    }
    else if (argc == 2)
    {
    	pszPath = argv[1];
    }
    else
    {
        Usage();
    }

    if (pszPath[0] != '/' && pszPath[0] != '\\')
    {
        fprintf(stderr,"Error: Directory or file path must be absolute\n");
        Usage();
    }

    status = LwRtlCStringAllocatePrintf(    
                    &pszFsPath,
                    "%s%s",
                    pszFsRoot,
                    pszPath);
    GOTO_CLEANUP_ON_STATUS_EE(status, EE);

    status = BuildDefaultShareSD(&pRelSecDesc, &ulRelSecDescLen, extraAccess);
    GOTO_CLEANUP_ON_STATUS_EE(status, EE);
    
    status = RtlUnicodeStringAllocateFromCString(&fileName.Name, pszFsPath);
    GOTO_CLEANUP_ON_STATUS_EE(status, EE);

    status = NtCreateFile(
                    &fileHandle,
                    /*AsyncControlBlock*/NULL,
                    &ioStatusBlock,
                    &fileName,
                    /*SecurityDescriptor*/NULL,
                    /*SecurityQOS*/NULL,
                    WRITE_OWNER|WRITE_DAC|READ_CONTROL,
                    /*AllocationSize*/0,
                    /*FileAttributes*/0,
                    /*ShareAccess*/0,
                    FILE_OPEN,
                    FILE_DIRECTORY_FILE,
                    /*pEaBuffer*/NULL,
                    /*EaLength*/0,
                    /*pEcpList*/NULL,
                    /*PIO Creds*/NULL);
    LWIO_ASSERT(IS_BOTH_OR_NEITHER(NT_SUCCESS(status), fileHandle));
    GOTO_CLEANUP_ON_STATUS_EE(status, EE);

    LWIO_LOG_ALWAYS("Opened file '%s'", pszPath);

    status = NtSetSecurityFile(
                   fileHandle,
                   NULL,
                   &ioStatusBlock,
                   secInfo,
                   pRelSecDesc,
                   ulRelSecDescLen);
    GOTO_CLEANUP_ON_STATUS_EE(status, EE);

    LWIO_LOG_ALWAYS("SetSecurity on file '%s'", pszPath);

cleanup:
    if (fileHandle)
    {
        NtCloseFile(fileHandle);
    }

    LW_RTL_UNICODE_STRING_FREE(&fileName.Name);
    RTL_FREE(&pszFsPath);
    RTL_FREE(&pRelSecDesc);

    LOG_LEAVE_IF_STATUS_EE(status, EE);
    return status;
}
