/*++

Copyright (c) 1995  Microsoft Corporation
All rights reserved.

Module Name:

    prtsec.cxx

Abstract:

    Print queue administration.

Author:

    Albert Ting (AlbertT)  28-Aug-1995

Environment:

Revision History:

    28-Aug-1995 AlbertT     Munged from prtq32.c.

--*/

#include "precomp.hxx"
#pragma hdrstop

#include "time.hxx"
#include "splsetup.h"
#include "psetup.hxx"
#include "instarch.hxx"
#include "portslv.hxx"
#include "prtprop.hxx"

#ifdef SECURITY
#ifndef UNICODE
#error "Acledit entrypoints are Unicode only."
#endif

LPCTSTR gpszAclEdit = TEXT( "acledit.dll" );

LPCSTR gpszSedDiscretionaryAclEditor = "SedDiscretionaryAclEditor";
LPCSTR gpszSedSystemAclEditor        = "SedSystemAclEditor";
LPCSTR gpszSedTakeOwnership          = "SedTakeOwnership";


HINSTANCE
TPrinterSecurity::ghLibraryAcledit;

TPrinterSecurity::PFNSED_DISCRETIONARY_ACL_EDITOR
TPrinterSecurity::gpfnSedDiscretionaryAclEditor;

TPrinterSecurity::PFNSED_SYSTEM_ACL_EDITOR
TPrinterSecurity::gpfnSedSystemAclEditor;

TPrinterSecurity::PFNSED_TAKE_OWNERSHIP
TPrinterSecurity::gpfnSedTakeOwnership;


GENERIC_MAPPING
TPrinterSecurity::gGenericMappingPrinters = {
    PRINTER_READ,
    PRINTER_WRITE,
    PRINTER_EXECUTE,
    PRINTER_ALL_ACCESS
};

GENERIC_MAPPING
TPrinterSecurity::gGenericMappingDocuments = {
    JOB_READ,
    JOB_WRITE,
    JOB_EXECUTE,
    JOB_ALL_ACCESS
};

SED_HELP_INFO
TPrinterSecurity::gHelpInfoPermissions = {
    NULL,
    {
        ID_HELP_PERMISSIONS_MAIN_DLG,
        0,
        0,
        ID_HELP_PERMISSIONS_ADD_USER_DLG,
        ID_HELP_PERMISSIONS_LOCAL_GROUP,
        ID_HELP_PERMISSIONS_GLOBAL_GROUP,
        ID_HELP_PERMISSIONS_FIND_ACCOUNT
    }
};

SED_HELP_INFO
TPrinterSecurity::gHelpInfoAuditing = {
    NULL,
    {
        ID_HELP_AUDITING_MAIN_DLG,
        0,
        0,
        ID_HELP_AUDITING_ADD_USER_DLG,
        ID_HELP_AUDITING_LOCAL_GROUP,
        ID_HELP_AUDITING_GLOBAL_GROUP,
        ID_HELP_AUDITING_FIND_ACCOUNT
    }
};

SED_HELP_INFO
TPrinterSecurity::gHelpInfoTakeOwnership = {
    NULL,
    {
        ID_HELP_TAKE_OWNERSHIP
    }
};


SED_OBJECT_TYPE_DESCRIPTOR
TPrinterSecurity::gObjectTypeDescriptor = {
    SED_REVISION1,                                // Revision
    TRUE,                                         // IsContainer
    TRUE,                                         // AllowNewObjectPerms
    TRUE,                                         // MapSpecificPermsToGeneric
    &TPrinterSecurity::gGenericMappingPrinters,  // GenericMapping
    &TPrinterSecurity::gGenericMappingDocuments, // GenericMappingNewObjects
    NULL,                                         // ObjectTypeName
    NULL,                                         // HelpInfo
    NULL,                                         // ApplyToSubContainerTitle
    NULL,                                         // ApplyToObjectsTitle
    NULL,                                         // ApplyToSubContainerConfirmation
    NULL,                                         // SpecialObjectAccessTitle
    NULL                                          // SpecialNewObjectAccessTitle
};

//
// Application accesses passed to the discretionary ACL editor
// as well as the Take Ownership dialog.
//
SED_APPLICATION_ACCESS
TPrinterSecurity::gpDiscretionaryAccessGroup[TPrinterSecurity::PERMS_COUNT] = {

    //
    // No Access:
    //
    {
        SED_DESC_TYPE_CONT_AND_NEW_OBJECT,  // Type
        0,                                  // AccessMask1
        0,                                  // AccessMask2
        NULL                                // PermissionTitle
    },

    //
    // Print permission:
    //
    {
        SED_DESC_TYPE_CONT_AND_NEW_OBJECT,
        GENERIC_EXECUTE | GENERIC_READ | GENERIC_WRITE,
        ACCESS_MASK_NEW_OBJ_NOT_SPECIFIED,
        NULL
    },

    //
    // Document Administer permission:
    //
    {
        SED_DESC_TYPE_CONT_AND_NEW_OBJECT,
        STANDARD_RIGHTS_READ,
        GENERIC_ALL,
        NULL
    },

    //
    // Administer permission:
    //
    {
        SED_DESC_TYPE_CONT_AND_NEW_OBJECT,
        GENERIC_ALL,
        GENERIC_ALL,
        NULL
    }
};

//
// Application accesses passed to the system ACL editor:
//
SED_APPLICATION_ACCESS
TPrinterSecurity::gpSystemAccessGroup[TPrinterSecurity::PERMS_AUDIT_COUNT] = {

    //
    // Print permission:
    //
    {
        SED_DESC_TYPE_AUDIT,
        PRINTER_ACCESS_USE,
        ACCESS_MASK_NEW_OBJ_NOT_SPECIFIED,
        NULL
    },

    {
        SED_DESC_TYPE_AUDIT,
        PRINTER_ACCESS_ADMINISTER | ACCESS_SYSTEM_SECURITY,
        ACCESS_MASK_NEW_OBJ_NOT_SPECIFIED,
        NULL
    },

    {
        SED_DESC_TYPE_AUDIT,
        DELETE,
        ACCESS_MASK_NEW_OBJ_NOT_SPECIFIED,
        NULL
    },

    {
        SED_DESC_TYPE_AUDIT,
        WRITE_DAC,
        ACCESS_MASK_NEW_OBJ_NOT_SPECIFIED,
        NULL
    },

    {
        SED_DESC_TYPE_AUDIT,
        WRITE_OWNER,
        ACCESS_MASK_NEW_OBJ_NOT_SPECIFIED,
        NULL
    }
};

TString TPrinterSecurity::gstrPrinter;

/********************************************************************

    Security.

********************************************************************/

BOOL
TPrinterSecurity::
bInitStrings(
    VOID
    )
{
    //
    // Check whether the gstrPrinter has been loaded with the
    // IDS_PRINTER string.
    //
    if( !gstrPrinter.bEmpty( )){
        return TRUE;
    }

    gHelpInfoPermissions.pszHelpFileName    = (LPTSTR)gszWindowsHlp;
    gHelpInfoAuditing.pszHelpFileName       = (LPTSTR)gszWindowsHlp;
    gHelpInfoTakeOwnership.pszHelpFileName  = (LPTSTR)gszWindowsHlp;

    if( !gstrPrinter.bLoadString( ghInst, IDS_PRINTER )){
        return FALSE;
    }

    gObjectTypeDescriptor.ObjectTypeName = (LPTSTR)(LPCTSTR)gstrPrinter;

    gpDiscretionaryAccessGroup[PERMS_NOACC].PermissionTitle =
        pszLoadString( ghInst, IDS_SEC_NOACCESS );

    gpDiscretionaryAccessGroup[PERMS_PRINT].PermissionTitle =
        pszLoadString( ghInst, IDS_SEC_PRINT );

    gpDiscretionaryAccessGroup[PERMS_DOCAD].PermissionTitle =
        pszLoadString( ghInst, IDS_SEC_ADMINISTERDOCUMENTS );

    gpDiscretionaryAccessGroup[PERMS_ADMIN].PermissionTitle =
        pszLoadString( ghInst, IDS_SEC_ADMINISTER );

    gpSystemAccessGroup[PERMS_AUDIT_PRINT].PermissionTitle =
        pszLoadString( ghInst, IDS_SEC_AUDIT_PRINT );

    gpSystemAccessGroup[PERMS_AUDIT_ADMINISTER].PermissionTitle =
        pszLoadString( ghInst, IDS_SEC_AUDIT_ADMINISTER );

    gpSystemAccessGroup[PERMS_AUDIT_DELETE].PermissionTitle =
        pszLoadString( ghInst, IDS_SEC_AUDIT_DELETE );

    gpSystemAccessGroup[PERMS_AUDIT_CHANGE_PERMISSIONS].PermissionTitle =
        pszLoadString( ghInst, IDS_SEC_CHANGE_PERMISSIONS );

    gpSystemAccessGroup[PERMS_AUDIT_TAKE_OWNERSHIP].PermissionTitle =
        pszLoadString( ghInst, IDS_SEC_TAKE_OWNERSHIP );

    return TRUE;
}


BOOL
TPrinterSecurity::
bHandleMessage(
    UINT uMsg,
    WPARAM wParam,
    LPARAM lParam
    )
{
    switch( uMsg ){
    case WM_INITDIALOG:

        if( !bInitStrings( )){
            DBGMSG( DBG_ERROR,
                    ( "PrinterSecurity.bHandlMessage: InitStrings failed %d\n",
                      GetLastError( )));
        }

        return TRUE;

    case WM_HELP:
    case WM_CONTEXTMENU:
        return PrintUIHelp( uMsg, _hDlg, wParam, lParam );

    case WM_DESTROY:

        return TRUE;

    case WM_COMMAND:

        switch( GET_WM_COMMAND_ID( wParam, lParam )){
        case IDC_SEC_PERMS:

            vCallDiscretionaryAclEditor();
            break;

        case IDC_SEC_AUDIT:

            vCallSystemAclEditor();
            break;

        case IDC_SEC_OWNER:

            vCallTakeOwnershipDialog();
            break;
        }
    }

    return FALSE;
}


/********************************************************************

    Load the dll.

********************************************************************/

BOOL
TPrinterSecurity::
bLoadAcledit(
    VOID
    )

/*++

Routine Description:

    Loads acledit and sets pfns.

    Note: Not multithread safe, no unloading done.

Arguments:

Return Value:

    TRUE = success, FALSE = error.

--*/
{
    if( ghLibraryAcledit ){
        return TRUE;
    }

    ghLibraryAcledit = LoadLibrary( gpszAclEdit );

    if( !ghLibraryAcledit ){
        goto Fail;
    }

    gpfnSedDiscretionaryAclEditor =
        (PFNSED_DISCRETIONARY_ACL_EDITOR)GetProcAddress(
            ghLibraryAcledit,
            gpszSedDiscretionaryAclEditor );

    gpfnSedSystemAclEditor =
        (PFNSED_SYSTEM_ACL_EDITOR)GetProcAddress(
            ghLibraryAcledit,
            gpszSedSystemAclEditor );

    gpfnSedTakeOwnership =
        (PFNSED_TAKE_OWNERSHIP)GetProcAddress(
            ghLibraryAcledit,
            gpszSedTakeOwnership );

    if( !gpfnSedDiscretionaryAclEditor ||
        !gpfnSedSystemAclEditor        ||
        !gpfnSedTakeOwnership ){

        FreeLibrary( ghLibraryAcledit );
        ghLibraryAcledit = NULL;
        goto Fail;
    }
    return TRUE;

Fail:
    vShowUnexpectedError( NULL, IDS_ERR_PRINTER_PROP_TITLE );
    return FALSE;
}

/********************************************************************

    Bring up each of the dialogs.

********************************************************************/

VOID
TPrinterSecurity::
vCallDiscretionaryAclEditor(
    VOID
    )

/*++

Routine Description:

    Edit access privileges of print queue.

Arguments:

Return Values:

--*/

{
    HANDLE hPrinterWriteDac = NULL;;
    DWORD dwAccess = WRITE_DAC;
    SED_APPLICATION_ACCESSES ApplicationAccesses;
    DWORD SedStatus;
    TStatusB bStatus;
    TStatus Status;

    Status DBGNOCHK = 0;

    PPRINTER_INFO_3 pInfo3 = NULL;
    DWORD cbInfo3 = 0;

    if( !bLoadAcledit( )){
        goto Fail;
    }

    //
    // Get the security descriptor.
    //
    if( !_pPrinterData->hPrinter( )){

        Status DBGCHK = ERROR_ACCESS_DENIED;
        goto Fail;
    }

    bStatus DBGCHK = VDataRefresh::bGetPrinter( _pPrinterData->hPrinter(),
                                                3,
                                                (PVOID*)&pInfo3,
                                                &cbInfo3 );
    if( !bStatus ){

        SPLASSERT( !pInfo3 );

        Status DBGCHK = GetLastError();
        goto Fail;
    }

    SECURITY_CONTEXT SecurityContext;

    Status DBGCHK = TPrinter::sOpenPrinter( _pPrinterData->strPrinterName(),
                                            &dwAccess,
                                            &hPrinterWriteDac );
    if( Status == ERROR_SUCCESS){
        SPLASSERT( hPrinterWriteDac );
        SecurityContext.hPrinter = hPrinterWriteDac;
    } else {
        SPLASSERT( !hPrinterWriteDac );
        SecurityContext.hPrinter = _pPrinterData->hPrinter();
    }

    SecurityContext.SecurityInformation = DACL_SECURITY_INFORMATION;
    SecurityContext.pPrinterSecurity = this;

    //
    // Pass all the permissions to the ACL editor,
    // and set up the type required:
    //
    ApplicationAccesses.Count = PERMS_COUNT;
    ApplicationAccesses.AccessGroup = gpDiscretionaryAccessGroup;
    ApplicationAccesses.DefaultPermName =
        gpDiscretionaryAccessGroup[PERMS_PRINT].PermissionTitle;

    COUNT i;
    for( i = 0; i < PERMS_COUNT; ++i ){
        ApplicationAccesses.AccessGroup[i].Type =
            SED_DESC_TYPE_CONT_AND_NEW_OBJECT;
    }

    SED_OBJECT_TYPE_DESCRIPTOR Descriptor = gObjectTypeDescriptor;

    Descriptor.AllowNewObjectPerms = TRUE;
    Descriptor.HelpInfo = &gHelpInfoPermissions;

    Status DBGCHK = (*gpfnSedDiscretionaryAclEditor)(
                        _hDlg,
                        ghInst,
                        (LPTSTR)(LPCTSTR)_pPrinterData->strServerName(),
                        &Descriptor,
                        &ApplicationAccesses,
                        (LPTSTR)(LPCTSTR)_pPrinterData->strPrinterName(),
                        SedCallback2,
                        (DWORD)&SecurityContext,
                        pInfo3->pSecurityDescriptor,
                        FALSE,
                        (BOOLEAN)!hPrinterWriteDac,
                        &SedStatus,
                        0 );

Fail:

    if( Status ){
        SetLastError( Status );
        vShowUnexpectedError( NULL, IDS_ERR_PRINTER_PROP_TITLE );
    }

    //
    // Close the printer use to write the Dac.
    //
    if( hPrinterWriteDac ){
        ClosePrinter( hPrinterWriteDac );
    }

    FreeMem( pInfo3 );
}

VOID
TPrinterSecurity::
vCallSystemAclEditor(
    VOID
    )

/*++

Routine Description:

    Edit auditing properties of print queue.

Arguments:

Return Values:

--*/

{
    HANDLE hPrinterSystemAccess = NULL;
    SED_APPLICATION_ACCESSES ApplicationAccesses;
    DWORD SedStatus;
    TStatusB bStatus;
    TStatus Status;

    Status DBGNOCHK = 0;

    if( !bLoadAcledit( )){
        return;
    }

    {
        TAcquirePrivilege AcquirePrivilege( SE_SECURITY_NAME );

        if( !VALID_OBJ( AcquirePrivilege )){
            vShowUnexpectedError( NULL, IDS_ERR_PRINTER_PROP_TITLE );
            return;
        }

        DWORD dwAccess = ACCESS_SYSTEM_SECURITY;
        Status DBGCHK = TPrinter::sOpenPrinter(
                                      _pPrinterData->strPrinterName(),
                                      &dwAccess,
                                      &hPrinterSystemAccess );

        if( Status != ERROR_SUCCESS ){
            vShowUnexpectedError( NULL, IDS_ERR_PRINTER_PROP_TITLE );
            return;
        }
    }

    //
    // Get the security descriptor.
    //
    PPRINTER_INFO_3 pInfo3 = NULL;
    DWORD cbInfo3 = 0;

    bStatus DBGCHK = VDataRefresh::bGetPrinter( hPrinterSystemAccess,
                                                3,
                                                (PVOID*)&pInfo3,
                                                &cbInfo3 );
    if( !bStatus ){
        Status DBGCHK = GetLastError();
        goto Fail;
    }

    SECURITY_CONTEXT SecurityContext;

    SecurityContext.SecurityInformation = SACL_SECURITY_INFORMATION;
    SecurityContext.pPrinterSecurity = this;

    SPLASSERT( hPrinterSystemAccess );
    SecurityContext.hPrinter = hPrinterSystemAccess;

    //
    // Pass only the Print and Administer permissions to the ACL editor,
    // and set up the type required:
    //
    ApplicationAccesses.Count = PERMS_AUDIT_COUNT;
    ApplicationAccesses.AccessGroup = gpSystemAccessGroup;
    ApplicationAccesses.DefaultPermName =
        gpDiscretionaryAccessGroup[PERMS_PRINT].PermissionTitle;

    SED_OBJECT_TYPE_DESCRIPTOR Descriptor = gObjectTypeDescriptor;

    Descriptor.AllowNewObjectPerms = FALSE;
    Descriptor.HelpInfo = &gHelpInfoAuditing;

    Status DBGCHK = (*gpfnSedSystemAclEditor)(
                        _hDlg,
                        ghInst,
                        (LPTSTR)(LPCTSTR)_pPrinterData->strServerName(),
                        &Descriptor,
                        &ApplicationAccesses,
                        (LPTSTR)(LPCTSTR)_pPrinterData->strPrinterName(),
                        SedCallback2,
                        (DWORD)&SecurityContext,
                        pInfo3->pSecurityDescriptor,
                        FALSE,
                        &SedStatus,
                        0 );

Fail:

    if( Status ){
        SetLastError( Status );
        vShowUnexpectedError( NULL, IDS_ERR_PRINTER_PROP_TITLE );
    }

    FreeMem( pInfo3 );

    if( hPrinterSystemAccess ){
        ClosePrinter( hPrinterSystemAccess );
    }
}


VOID
TPrinterSecurity::
vCallTakeOwnershipDialog(
    VOID
    )

/*++

Routine Description:

    Edit ownership of print queue.

    !! BUGBUG !!

    How does a user get to this dialog if they can't get properties
    on a printer?

Arguments:

Return Values:

--*/

{
    SED_APPLICATION_ACCESSES ApplicationAccesses;
    DWORD SedStatus;
    TStatusB bStatus;
    TStatus Status;
    HANDLE hPrinterWriteOwner = NULL;

    Status DBGNOCHK = 0;

    if( !bLoadAcledit( )){
        return;
    }

    //
    // Get the security descriptor.
    //
    PPRINTER_INFO_3 pInfo3 = NULL;
    DWORD cbInfo3 = 0;

    //
    // Attempt to retrieve the previous owner.
    //
    if( _pPrinterData->hPrinter( )){

        bStatus DBGCHK = VDataRefresh::bGetPrinter( _pPrinterData->hPrinter(),
                                                    3,
                                                    (PVOID*)&pInfo3,
                                                    &cbInfo3 );
    }

    {
        TAcquirePrivilege AcquirePrivilege( SE_TAKE_OWNERSHIP_NAME );
        SECURITY_CONTEXT SecurityContext;
        DWORD dwAccess = WRITE_OWNER;

        Status DBGCHK = TPrinter::sOpenPrinter( _pPrinterData->strPrinterName(),
                                                &dwAccess,
                                                &hPrinterWriteOwner );
        if( Status == ERROR_SUCCESS){
            SPLASSERT( hPrinterWriteOwner );
            SecurityContext.hPrinter = hPrinterWriteOwner;
        } else {
            SPLASSERT( !hPrinterWriteOwner );
            SecurityContext.hPrinter = _pPrinterData->hPrinter();
        }

        SecurityContext.SecurityInformation = OWNER_SECURITY_INFORMATION;
        SecurityContext.pPrinterSecurity = this;

        ApplicationAccesses.Count = PERMS_COUNT;
        ApplicationAccesses.AccessGroup = gpDiscretionaryAccessGroup;
        ApplicationAccesses.DefaultPermName =
            gpDiscretionaryAccessGroup[PERMS_PRINT].PermissionTitle;

        COUNT i;
        for( i = 0; i < PERMS_COUNT; ++i ){
            ApplicationAccesses.AccessGroup[i].Type = SED_DESC_TYPE_AUDIT;
        }

        BOOL bCantReadOwner;
        PSECURITY_DESCRIPTOR pSecurityDescriptor;

        bCantReadOwner = pInfo3 ? FALSE : TRUE;
        pSecurityDescriptor = pInfo3 ?
                                  pInfo3->pSecurityDescriptor :
                                  NULL;

        Status DBGCHK = (*gpfnSedTakeOwnership)(
                            _hDlg,
                            ghInst,
                            (LPTSTR)(LPCTSTR)_pPrinterData->strServerName(),
                            (LPTSTR)(LPCTSTR)gstrPrinter,
                            (LPTSTR)(LPCTSTR)_pPrinterData->strPrinterName(),
                            1,
                            SedCallback2,
                            (DWORD)&SecurityContext,
                            pSecurityDescriptor,
                            (BOOLEAN)bCantReadOwner,
                            (BOOLEAN)!hPrinterWriteOwner,
                            &SedStatus,
                            &gHelpInfoTakeOwnership,
                            0 );
    }

    if( hPrinterWriteOwner ){
        ClosePrinter( hPrinterWriteOwner );
    }

    FreeMem( pInfo3 );
}


/********************************************************************

    Security callback routine.

********************************************************************/

DWORD
TPrinterSecurity::
SedCallback2(
    HWND                 hwndParent,
    HANDLE               hInstance,
    DWORD                dwCallBackContext,
    PSECURITY_DESCRIPTOR psdUpdated,
    PSECURITY_DESCRIPTOR pSecDescNewObjects,
    BOOLEAN              bApplyToSubContainers,
    BOOLEAN              bApplyToSubObjects,
    LPDWORD              pdwStatusReturn
    )

/*++

Routine Description:

    Called by acledit to process writes.

Arguments:

    <insert>.

Return Values:

    <insert>.

--*/

{
    UNREFERENCED_PARAMETER( pdwStatusReturn );
    UNREFERENCED_PARAMETER( bApplyToSubObjects );
    UNREFERENCED_PARAMETER( bApplyToSubContainers );
    UNREFERENCED_PARAMETER( pSecDescNewObjects );
    UNREFERENCED_PARAMETER( hInstance );
    UNREFERENCED_PARAMETER( hwndParent );

    PSECURITY_CONTEXT pSecurityContext;
    SECURITY_DESCRIPTOR  SecurityDescriptorNew;
    PSECURITY_DESCRIPTOR pSelfRelativeSD = NULL;
    DWORD                cbSelfRelativeSD;

    TStatusB bStatus;

    pSecurityContext = (PSECURITY_CONTEXT)dwCallBackContext;
    SPLASSERT( pSecurityContext->hPrinter );

    if( InitializeSecurityDescriptor( &SecurityDescriptorNew,
                                      SECURITY_DESCRIPTOR_REVISION1 ) &&
        BuildNewSecurityDescriptor( &SecurityDescriptorNew,
                                    pSecurityContext->SecurityInformation,
                                    psdUpdated )){

        pSelfRelativeSD = AllocCopySecurityDescriptor( &SecurityDescriptorNew,
                                                       &cbSelfRelativeSD );
    } else {

        DBGMSG( DBG_ERROR, ( "PrinterSecurity.SedCallback2: InitializeSD failedt %d\n",
                             GetLastError( )));
    }

    if( !pSelfRelativeSD ){

        DBGMSG( DBG_WARN,
                ( "PrinterSecurity.SedCallback2: Alloc copy sd failed %d\n",
                  GetLastError( )));

        SPLASSERT( GetLastError( ));

        vShowUnexpectedError( NULL, IDS_ERR_PRINTER_PROP_TITLE );
        return GetLastError();
    }

    PRINTER_INFO_3 PrinterInfo3;

    PrinterInfo3.pSecurityDescriptor = pSelfRelativeSD;

    bStatus DBGCHK = SetPrinter( pSecurityContext->hPrinter,
                                 3,
                                 (PBYTE)&PrinterInfo3,
                                 0 );
    //
    // Free the newly created sd.
    //
    FreeMem( pSelfRelativeSD );

    if( !bStatus ){

        SPLASSERT( GetLastError( ));

        iMessage( NULL,
                  IDS_ERR_PRINTER_PROP_TITLE,
                  IDS_ERR_SAVE_PRINTER,
                  MB_OK|MB_ICONHAND,
                  kMsgGetLastError,
                  NULL );

        return GetLastError();
    }

    //
    // Refresh the property page set.
    //
    pSecurityContext->pPrinterSecurity->vReloadPages();

    return ERROR_SUCCESS;
}


/********************************************************************

    Helpers.

********************************************************************/


BOOL
TPrinterSecurity::
BuildNewSecurityDescriptor(
    PSECURITY_DESCRIPTOR psdNew,
    SECURITY_INFORMATION SecurityInformation,
    PSECURITY_DESCRIPTOR psdUpdated
    )

/*++

Routine Description:

    Builds new security desriptor.

Arguments:

Return Values:

--*/

{
    BOOL bDefaulted = FALSE;
    PSID pOwnerSid = NULL;
    PSID pGroupSid = NULL;
    BOOL bDaclPresent = FALSE;
    PACL pDacl = NULL;
    BOOL bSaclPresent = FALSE;
    PACL pSacl = NULL;
    TStatusB bStatus;

    switch( SecurityInformation ){
    case OWNER_SECURITY_INFORMATION:

        if( GetSecurityDescriptorOwner( psdUpdated,
                                        &pOwnerSid,
                                        &bDefaulted )){

            bStatus DBGCHK = SetSecurityDescriptorOwner( psdNew,
                                                         pOwnerSid,
                                                         bDefaulted );
        }
        break;

    case DACL_SECURITY_INFORMATION:

        if( GetSecurityDescriptorDacl( psdUpdated,
                                       &bDaclPresent,
                                       &pDacl,
                                       &bDefaulted )) {

            bStatus DBGCHK = SetSecurityDescriptorDacl( psdNew,
                                                        bDaclPresent,
                                                        pDacl,
                                                        bDefaulted );
        }
        break;

    case SACL_SECURITY_INFORMATION:

        if( GetSecurityDescriptorSacl( psdUpdated,
                                       &bSaclPresent,
                                       &pSacl,
                                       &bDefaulted )) {

            bStatus DBGCHK = SetSecurityDescriptorSacl( psdNew,
                                                        bSaclPresent,
                                                        pSacl,
                                                        bDefaulted );
        }
        break;

    default:

        DBGMSG( DBG_ERROR, ( "PrinterSecurity.BuildSD: Unknown type %d\n",
                SecurityInformation ));
        return FALSE;
    }

    return bStatus;
}


PSECURITY_DESCRIPTOR
TPrinterSecurity::
AllocCopySecurityDescriptor(
    IN     PSECURITY_DESCRIPTOR pSecurityDescriptor,
       OUT PDWORD pdwLength
    )

/*++

Routine Description:

    Alloc copy of security descriptor.

Arguments:

    pSecurityDescriptor - sd to copy.

    pdwLength - Output length.

Return Value:

    Newly allocated sd.  NULL if failed.

--*/

{
    PSECURITY_DESCRIPTOR psdCopy;
    DWORD dwLength;

    dwLength = GetSecurityDescriptorLength(pSecurityDescriptor);

    psdCopy = AllocMem( dwLength );

    if( psdCopy ){

        MakeSelfRelativeSD( pSecurityDescriptor,
                            psdCopy,
                            &dwLength);

        *pdwLength = dwLength;
    }

    return psdCopy;
}

#endif // def SECURITY