/*++

Copyright (c) 1996 Microsoft Corporation

Module Name:

    setupsrv.cpp

Abstract:

    Routines for secedit integration with system setup and component setup

Author:

    Jin Huang (jinhuang) 15-Aug-1997

Revision History:

    jinhuang 26-Jan-1998  splitted to client-server

--*/

#include "headers.h"
#include "serverp.h"
#include "srvrpcp.h"
#include "pfp.h"
#include <io.h>


SCESTATUS
ScepUpdateObjectInSection(
    IN PSCECONTEXT hProfile,
    IN SCETYPE ProfileType,
    IN PWSTR ObjectName,
    IN SE_OBJECT_TYPE ObjectType,
    IN UINT nFlag,
    IN PWSTR SDText,
    OUT UINT *pStatus
    );

//
// implementations
//


DWORD
ScepSetupUpdateObject(
    IN PSCECONTEXT Context,
    IN PWSTR ObjectFullName,
    IN SE_OBJECT_TYPE ObjectType,
    IN UINT nFlag,
    IN PWSTR SDText
    )
/*
Routine Description:

    This routine is the private API called from the RPC interface to
    update object information in the database.

Arguments:

    Context     - the database context handle

    ObjectFullName  - the object's name

    Objecttype      - the object type

    nFlag       - the flag on how to update this object

    SDText      - the security descriptor in SDDL text


Return Value:

*/
{

    if ( !ObjectFullName || NULL == SDText ) {
        return ERROR_INVALID_PARAMETER;
    }

    switch ( ObjectType ) {
    case SE_SERVICE:
    case SE_REGISTRY_KEY:
    case SE_FILE_OBJECT:
        break;
    default:
        return ERROR_INVALID_PARAMETER;
    }

    SCESTATUS rc;

    DWORD dwInSetup=0;

    ScepRegQueryIntValue(HKEY_LOCAL_MACHINE,
                TEXT("System\\Setup"),
                TEXT("SystemSetupInProgress"),
                &dwInSetup
                );

    //
    // convert SDText to security descriptor
    //
    PSECURITY_DESCRIPTOR pSD=NULL;
    DWORD SDSize;
    SECURITY_INFORMATION SeInfo=0;
    HANDLE  Token=NULL;

    DWORD Win32rc=ERROR_SUCCESS;

    if ( !(nFlag & SCESETUP_UPDATE_DB_ONLY) ) {
        //
        // security will be set, so compute the security descriptor
        //
        Win32rc = ConvertTextSecurityDescriptor (
                        SDText,
                        &pSD,
                        &SDSize,
                        &SeInfo
                        );

        if ( NO_ERROR == Win32rc ) {

            ScepChangeAclRevision(pSD, ACL_REVISION);

            //
            // get current thread/process's token
            //
            if (!OpenThreadToken( GetCurrentThread(),
                                   TOKEN_QUERY,
                                   FALSE,
                                   &Token)) {

                if (!OpenProcessToken( GetCurrentProcess(),
                                      TOKEN_QUERY,
                                      &Token)) {

                    Win32rc = GetLastError();
                }
            }

            if ( Token && (SeInfo & SACL_SECURITY_INFORMATION) ) {

                SceAdjustPrivilege( SE_SECURITY_PRIVILEGE, TRUE, Token );
            }
        }
    }

    if ( NO_ERROR == Win32rc ) {

        //
        // only update DB if it's in setup
        //

        //
        // on 64-bit platform, only update database if setup does not indicate SCE_SETUP_32KEY flag
        //

#ifdef _WIN64
        if ( dwInSetup && !(nFlag & SCE_SETUP_32KEY) ) {
#else
        if ( dwInSetup ) {
#endif

            // save this into SCP and SMP, do not overwrite the status/container flag
            // if there is one exist, else use SCE_STATUS_CHECK and check for container
            //
            //
            // start a transaction since there are multiple operations
            //

            rc = SceJetStartTransaction( Context );

            if ( rc == SCESTATUS_SUCCESS ) {

                UINT Status=SCE_STATUS_CHECK;

                rc = ScepUpdateObjectInSection(
                            Context,
                            SCE_ENGINE_SMP,
                            ObjectFullName,
                            ObjectType,
                            nFlag,
                            SDText,
                            &Status
                            );

                if ( rc == SCESTATUS_SUCCESS &&
                     (Context->JetSapID != JET_tableidNil) ) {
                    //
                    // the SAP table ID points to the tattoo table
                    // should update the tattoo table too if it exist
                    //
                    rc = ScepUpdateObjectInSection(
                                Context,
                                SCE_ENGINE_SAP,
                                ObjectFullName,
                                ObjectType,
                                nFlag,
                                SDText,
                                NULL
                                );

                }
            }

        } else {
            rc = SCESTATUS_SUCCESS;
        }

        if ( rc == SCESTATUS_SUCCESS &&
             !(nFlag & SCESETUP_UPDATE_DB_ONLY) ) {

            //
            // set security to the object
            //

            //
            // if 64-bit platform, no synchronization is done and setup will have
            // to call the exported API with SCE_SETUP_32KEY if 32-bit hive is desired
            //

#ifdef _WIN64
            if ( ObjectType == SE_REGISTRY_KEY && (nFlag & SCE_SETUP_32KEY) ){
                ObjectType = SE_REGISTRY_WOW64_32KEY;
            }
#endif

            Win32rc = ScepSetSecurityWin32(
                ObjectFullName,
                SeInfo,
                pSD,
                ObjectType
                );

        } else
            Win32rc = ScepSceStatusToDosError(rc);

        if ( Win32rc == ERROR_SUCCESS ||
             Win32rc == ERROR_FILE_NOT_FOUND ||
             Win32rc == ERROR_PATH_NOT_FOUND ||
             Win32rc == ERROR_INVALID_OWNER ||
             Win32rc == ERROR_INVALID_PRIMARY_GROUP ||
             Win32rc == ERROR_INVALID_HANDLE ) {

            if ( Win32rc )
                gWarningCode = Win32rc;

            if ( dwInSetup ) {  // in setup, update DB
                Win32rc = ScepSceStatusToDosError(
                           SceJetCommitTransaction( Context, 0));
            } else {
                Win32rc = ERROR_SUCCESS;
            }

        } else if ( dwInSetup ) {  // in setup

            SceJetRollback( Context, 0 );
        }

        if ( Token && (SeInfo & SACL_SECURITY_INFORMATION) )
            SceAdjustPrivilege( SE_SECURITY_PRIVILEGE, FALSE, Token );

    }

    CloseHandle(Token);

    if ( pSD ) {
        LocalFree(pSD);
        pSD = NULL;
    }

    return(Win32rc);

}


SCESTATUS
ScepUpdateObjectInSection(
    IN PSCECONTEXT Context,
    IN SCETYPE ProfileType,
    IN PWSTR ObjectName,
    IN SE_OBJECT_TYPE ObjectType,
    IN UINT nFlag,
    IN PWSTR SDText,
    OUT UINT *pStatus
    )
/*
Routine Description:

    Update SCP and SMP. if the table does not exist at all, ignore the update.
    Delete SAP entry for the object. If table or record not found, ignore the error.

Arguments:

Return Value:

*/
{
    if ( Context == NULL || ObjectName == NULL ) {
        return(SCESTATUS_INVALID_PARAMETER);
    }

    SCESTATUS rc;
    PSCESECTION hSection=NULL;
    PCWSTR SectionName;

    switch ( ObjectType ) {
    case SE_FILE_OBJECT:
        SectionName = szFileSecurity;
        break;
    case SE_REGISTRY_KEY:
        SectionName = szRegistryKeys;
        break;
    case SE_SERVICE:
        SectionName = szServiceGeneral;
        break;
    default:
        return(SCESTATUS_INVALID_PARAMETER);
    }

    rc = ScepOpenSectionForName(
                Context,
                ProfileType,
                SectionName,
                &hSection
                );
    if ( rc == SCESTATUS_BAD_FORMAT ||
         rc == SCESTATUS_RECORD_NOT_FOUND ) {
        return(SCESTATUS_SUCCESS);
    }

    if ( rc == SCESTATUS_SUCCESS ) {

        WCHAR         StatusFlag=L'\0';
        DWORD         ValueLen;
        BYTE          Status=SCE_STATUS_CHECK;
        BOOL          IsContainer=TRUE;
        BYTE          StartType;

        rc = SceJetGetValue(
                hSection,
                SCEJET_EXACT_MATCH_NO_CASE,
                ObjectName,
                NULL,
                0,
                NULL,
                (PWSTR)&StatusFlag,
                2,
                &ValueLen
                );

        if ( rc == SCESTATUS_SUCCESS ||
             rc == SCESTATUS_BUFFER_TOO_SMALL ) {

            rc = SCESTATUS_SUCCESS;

            Status = *((BYTE *)&StatusFlag);

            if ( pStatus ) {
                *pStatus = Status;
            }

            if ( ObjectType == SE_SERVICE ) {
                StartType = *((BYTE *)&StatusFlag+1);
            } else {
                IsContainer = *((CHAR *)&StatusFlag+1) != '0' ? TRUE : FALSE;
            }
        }

        if ( ObjectType == SE_SERVICE ) {

            DWORD SDLen, Len;
            PWSTR ValueToSet;

            StartType = (BYTE)nFlag;

            if ( SDText != NULL ) {
                SDLen = wcslen(SDText);
                Len = ( SDLen+1)*sizeof(WCHAR);
            } else
                Len = sizeof(WCHAR);

            ValueToSet = (PWSTR)ScepAlloc( (UINT)0, Len+sizeof(WCHAR) );

            if ( ValueToSet != NULL ) {

                //
                // The first byte is the flag, the second byte is IsContainer (1,0)
                //
                *((BYTE *)ValueToSet) = Status;

                *((BYTE *)ValueToSet+1) = StartType;

                if ( SDText != NULL ) {
                    swprintf(ValueToSet+1, L"%s", SDText );
                    ValueToSet[SDLen+1] = L'\0';  //terminate this string
                } else {
                    ValueToSet[1] = L'\0';
                }

                if ( SCESTATUS_SUCCESS == rc || ProfileType != SCE_ENGINE_SAP ) {
                    //
                    // only update tattoo table (pointed by SAP handle) if it finds a record there
                    // for other table (SMP), ignore the error code, just set
                    //
                    rc = SceJetSetLine( hSection,
                                        ObjectName,
                                        FALSE,
                                        ValueToSet,
                                        Len,
                                        0);
                }

                ScepFree( ValueToSet );

            } else {

                rc = SCESTATUS_NOT_ENOUGH_RESOURCE;
            }

        } else if ( SCESTATUS_SUCCESS == rc || ProfileType != SCE_ENGINE_SAP ) {
            //
            // only update tattoo table (pointed by SAP handle) if it finds a record there
            // for other table (SMP), ignore the error code, just set
            //

            rc = ScepSaveObjectString(
                    hSection,
                    ObjectName,
                    IsContainer,
                    Status,
                    SDText,
                    (SDText == NULL ) ? 0 : wcslen(SDText)
                    );
        }

        if ( rc == SCESTATUS_RECORD_NOT_FOUND )
            rc = SCESTATUS_SUCCESS;

        SceJetCloseSection(&hSection, TRUE);
    }

    return(rc);
}


DWORD
ScepSetupMoveFile(
    IN PSCECONTEXT Context,
    PWSTR OldName,
    PWSTR NewName OPTIONAL,
    PWSTR SDText OPTIONAL
    )
/*
Routine Description:

    Set security to OldName but save with NewName in SCE database if SDText
    is not NULL. If NewName is NULL, delete OldName from SCE database.

Arguments:

    Context     - the databaes context handle

    SectionName - the section name

    OldName     - the object's old name

    NewName     - the new name to rename to, if NULL, delete the old object

    SDText      - security string

Return Value:

    Win32 error code
*/
{

    if ( !Context || !OldName ) {
        return ERROR_INVALID_PARAMETER;
    }

    DWORD rc32=ERROR_SUCCESS;

    if ( NewName && SDText  ) {
        //
        // set security on OldName with SDText
        //

        rc32 = ScepSetupUpdateObject(
                        Context,
                        OldName,
                        SE_FILE_OBJECT,
                        0,
                        SDText
                        );
    }

    if ( rc32 == ERROR_SUCCESS ) {

        //
        // save this into SCP and SMP, do not overwrite the status/container flag
        // if there is one exist, else use SCE_STATUS_CHECK and check for container
        //

        SCESTATUS rc = SceJetStartTransaction( Context );

        if ( rc == SCESTATUS_SUCCESS ) {

            PSCESECTION hSection=NULL;
            //
            // process SMP section first
            //
            rc = ScepOpenSectionForName(
                        Context,
                        SCE_ENGINE_SMP,
                        szFileSecurity,
                        &hSection
                        );

            if ( rc == SCESTATUS_SUCCESS ) {
                if ( NewName ) {
                    //
                    // rename this line
                    //
                    rc = SceJetRenameLine(
                            hSection,
                            OldName,
                            NewName,
                            FALSE);

                } else {
                    //
                    // delete this line first
                    //
                    rc = SceJetDelete(
                        hSection,
                        OldName,
                        FALSE,
                        SCEJET_DELETE_LINE_NO_CASE
                        );
                }
                SceJetCloseSection( &hSection, TRUE);
            }


            if ( (SCESTATUS_SUCCESS == rc ||
                  SCESTATUS_RECORD_NOT_FOUND == rc ||
                  SCESTATUS_BAD_FORMAT == rc) &&
                 (Context->JetSapID != JET_tableidNil) ) {
                //
                // process tattoo table
                //
                rc = ScepOpenSectionForName(
                            Context,
                            SCE_ENGINE_SAP,
                            szFileSecurity,
                            &hSection
                            );

                if ( rc == SCESTATUS_SUCCESS ) {
                    if ( NewName ) {
                        //
                        // rename this line
                        //
                        rc = SceJetRenameLine(
                                hSection,
                                OldName,
                                NewName,
                                FALSE);

                    } else {
                        //
                        // delete this line first
                        //
                        rc = SceJetDelete(
                            hSection,
                            OldName,
                            FALSE,
                            SCEJET_DELETE_LINE_NO_CASE
                            );
                    }

                    SceJetCloseSection( &hSection, TRUE);
                }

            }
            if ( SCESTATUS_RECORD_NOT_FOUND == rc ||
                 SCESTATUS_BAD_FORMAT == rc ) {
                rc = SCESTATUS_SUCCESS;
            }

            if ( SCESTATUS_SUCCESS == rc ) {
                //
                // commit the transaction
                //
                rc = SceJetCommitTransaction( Context, 0 );
            } else {
                //
                // rollback the transaction
                //
                SceJetRollback( Context, 0 );
            }
        }

        rc32 = ScepSceStatusToDosError(rc);
    }

    return(rc32);
}