/*++

Copyright (c) 1994  Microsoft Corporation

Module Name:

    password.c

Abstract:

    functions to display & set the password policy for this workstation

Author:

    Bob Watson (a-robw)

Revision History:

    23 Dec 94

--*/
#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <ntsam.h>
#include <windows.h>
#include <tchar.h>
#include <stdlib.h>
#include <stdio.h>
#include <c2dll.h>
#include <c2inc.h>
#include <c2utils.h>
#include "c2funcs.h"
#include "c2funres.h"

// define action codes here. They are only meaningful in the
// context of this module.
#define AC_PW_LENGTH_NOCHANGE    0
#define AC_PW_LENGTH_UPDATE      1

#define SECURE C2DLL_C2

static
LONG
GetWorkstationMinPasswordLength (
)
{
    SAM_HANDLE  hsamObject;
    SAM_HANDLE  hsamDomain;
    PSID        psidDomain;

    SAM_ENUMERATE_HANDLE hSamEnum;
    PSAM_RID_ENUMERATION pRidEnum;

    PVOID   pvEnumBuffer;
    ULONG   ulEnumCount;

    LONG    lRetPwLen = -1; // init to error value

    NTSTATUS    ntstat;
    PDOMAIN_PASSWORD_INFORMATION pdpiData;

    SET_WAIT_CURSOR;
    // connect to SAM on this machine
    ntstat = SamConnect((PUNICODE_STRING)NULL, &hsamObject,
       SAM_SERVER_ALL_ACCESS, (POBJECT_ATTRIBUTES)NULL);
    if (ntstat == STATUS_SUCCESS) {
        // Ask SAM for the domains on this server.

        hSamEnum = 0;
        ntstat = SamEnumerateDomainsInSamServer(
            hsamObject,
            &hSamEnum,
            &pvEnumBuffer,
            1024,
            &ulEnumCount);

        if ((ntstat == STATUS_SUCCESS) || (ntstat == STATUS_MORE_ENTRIES)) {
            // look up only the first entry
            pRidEnum = (PSAM_RID_ENUMERATION) pvEnumBuffer;

            // get SID of domain 
            ntstat = SamLookupDomainInSamServer (
                hsamObject,
                &pRidEnum->Name,
                &psidDomain);

            if (ntstat == STATUS_SUCCESS) {
                // open handle to this domain
                ntstat = SamOpenDomain (
                    hsamObject,
                    DOMAIN_EXECUTE,
                    psidDomain,
                    &hsamDomain);

                if (ntstat == STATUS_SUCCESS) {
                    // get password policy for this domain
                    pdpiData = NULL;
                    ntstat = SamQueryInformationDomain (
                        hsamDomain,
                        DomainPasswordInformation,
                        (PVOID *)&pdpiData);

                    if (ntstat == STATUS_SUCCESS) {
                        // evaluate password length here
                        lRetPwLen = (LONG)pdpiData->MinPasswordLength;
                        ntstat = SamFreeMemory (pdpiData);
                    }
                    // close handle
                    SamCloseHandle (hsamDomain);
                }
            }
            SamFreeMemory(pvEnumBuffer);
        }
        SamCloseHandle (hsamObject);
    } else {
        lRetPwLen = -1;
    }
    SetLastError (RtlNtStatusToDosError(ntstat));
    SET_ARROW_CURSOR;
    return lRetPwLen;
}

static
BOOL
SetWorkstationMinPasswordLength (
    LONG    lMinLength
)
{
    SAM_HANDLE  hsamObject;
    SAM_HANDLE  hsamDomain;
    PSID        psidDomain;

    SAM_ENUMERATE_HANDLE hSamEnum;
    PSAM_RID_ENUMERATION pRidEnum;
                                                              
    PVOID   pvEnumBuffer;
    ULONG   ulEnumCount;
    BOOL    bReturn = FALSE; // assume error until everything works

    NTSTATUS    ntstat;
    PDOMAIN_PASSWORD_INFORMATION pdpiData;

    SET_WAIT_CURSOR;

    // connect to SAM on this machine
    if (EnableSecurityPriv()) {
        ntstat = SamConnect((PUNICODE_STRING)NULL, &hsamObject,
        STANDARD_RIGHTS_WRITE | SAM_SERVER_ALL_ACCESS,
        (POBJECT_ATTRIBUTES)NULL);
        if (ntstat == STATUS_SUCCESS) {
            // Ask SAM for the domains on this server.

            hSamEnum = 0;
            ntstat = SamEnumerateDomainsInSamServer(
                hsamObject,
                &hSamEnum,
                &pvEnumBuffer,
                1024,
                &ulEnumCount);

            if ((ntstat == STATUS_SUCCESS) || (ntstat == STATUS_MORE_ENTRIES)) {
                // look up only the first entry
                pRidEnum = (PSAM_RID_ENUMERATION) pvEnumBuffer;

                // get SID of domain 
                ntstat = SamLookupDomainInSamServer (
                    hsamObject,
                    &pRidEnum->Name,
                    &psidDomain);

                if (ntstat == STATUS_SUCCESS) {
                    // open handle to this domain
                    ntstat = SamOpenDomain (
                        hsamObject,
                        STANDARD_RIGHTS_WRITE               |
                           DOMAIN_READ_PASSWORD_PARAMETERS  |
                           DOMAIN_LIST_ACCOUNTS             |
                           DOMAIN_LOOKUP                    |
                           DOMAIN_WRITE_PASSWORD_PARAMS,
                        psidDomain,
                        &hsamDomain);

                    if (ntstat == STATUS_SUCCESS) {
                        // get password policy for this domain
                        pdpiData = NULL;
                        ntstat = SamQueryInformationDomain (
                            hsamDomain,
                            DomainPasswordInformation,
                            (PVOID *)&pdpiData);

                        if (ntstat == STATUS_SUCCESS) {
                            // evaluate password length here
                            lMinLength &= 0x000000FF;   // make it reallly short
                            pdpiData->MinPasswordLength = (USHORT)lMinLength;
            
                            ntstat =  SamSetInformationDomain (
                                hsamDomain,
                                DomainPasswordInformation,
                                (PVOID)pdpiData);
                        
                            if (ntstat == STATUS_SUCCESS) {
                                bReturn = TRUE;
                            }

                            ntstat = SamFreeMemory (pdpiData);
                        }
                        // close handle
                        SamCloseHandle (hsamDomain);
                    }
                }
                SamFreeMemory(pvEnumBuffer);
            }
            SamCloseHandle (hsamObject);
        }
        SetLastError (RtlNtStatusToDosError(ntstat));
    }

    SET_ARROW_CURSOR;
    
    return bReturn;
}

BOOL CALLBACK
C2PasswordLengthDlgProc(
    IN  HWND hDlg,           // window handle of the dialog box
	IN  UINT message,        // type of message
    IN  WPARAM  wParam,
    IN  LPARAM  lParam
)
/*++

Routine Description:

    Window procedure for Password Length Dialog Box

Arguments:

    Standard DlgProc arguments

ReturnValue:

    TRUE    the message was handled by this routine
    FALSE   DefDialogProc should handle the message

--*/
{
    static  PLONG   plNewLength;   // save address of caller's data block
    DWORD   dwLogSetting = 0;
    int     nButton;
    int     nState;
    LONG    lPasswordLength;
    TCHAR   szPasswordLength[4];

    switch (message) {
        case WM_INITDIALOG:
            // save the pointer to the new value
            plNewLength = (PLONG)lParam;

            // set the correct radio button
            lPasswordLength = GetWorkstationMinPasswordLength();

            if (lPasswordLength > 0) {
                nButton = IDC_MIN_PASSWORD_LENGTH;
                EnableWindow (GetDlgItem(hDlg, IDC_PASSWORD_LENGTH_EDIT), TRUE);
                _stprintf (szPasswordLength, TEXT("%2d"), lPasswordLength);
                SetDlgItemText (hDlg, IDC_PASSWORD_LENGTH_EDIT, szPasswordLength);
            } else {
                nButton = IDC_ALLOW_BLANK_PASSWORD;
                EnableWindow (GetDlgItem(hDlg, IDC_PASSWORD_LENGTH_EDIT), FALSE);
            }

            CheckRadioButton (hDlg,
                IDC_ALLOW_BLANK_PASSWORD,
                IDC_MIN_PASSWORD_LENGTH,
                nButton);

            // set text limits on edit boxes
            SendDlgItemMessage (hDlg, IDC_PASSWORD_LENGTH_EDIT, EM_LIMITTEXT,
                (WPARAM)2, 0);

            SetFocus (GetDlgItem (hDlg, IDOK)); // set focus to OK Button
            return FALSE; // we don't want Windows to set the focus

        case WM_COMMAND:
            switch (LOWORD(wParam)){
                case IDOK:
                    if (HIWORD(wParam) == BN_CLICKED) {
                        if (IsDlgButtonChecked (hDlg, IDC_ALLOW_BLANK_PASSWORD) == CHECKED) {
                            *plNewLength = 0;
                            EndDialog (hDlg, (int)LOWORD(wParam));
                        } else {
                            GetDlgItemText (hDlg, IDC_PASSWORD_LENGTH_EDIT,
                                szPasswordLength, 4);
                            lPasswordLength = _tcstol(szPasswordLength, NULL, 10);

                            // make sure it's a valid length

                            if ((lPasswordLength > 0) && (lPasswordLength <= 14)) {
                                *plNewLength = lPasswordLength;
                                // then there's text so exit
                                EndDialog (hDlg, (int)LOWORD(wParam));
                            } else {
                                // an incorrect entry is in the edit box so display message
                                MessageBeep (MB_ICONASTERISK);
                                DisplayDllMessageBox (hDlg,
                                    IDS_PASSWORD_INVALID_LEN,
                                    IDS_PASSWORD_CAPTION,
                                    MBOK_INFO);
                                SetFocus (GetDlgItem (hDlg, IDC_PASSWORD_LENGTH_EDIT));
                            }
                        }
                        return TRUE;
                    } else {
                        return FALSE;
                    }

                case IDCANCEL:
                    if (HIWORD(wParam) == BN_CLICKED) {
                        // exit and return button that caused exit
                        EndDialog (hDlg, (int)LOWORD(wParam));
                        return TRUE;
                    } else {
                        return FALSE;
                    }

                case IDC_C2:
                    if (HIWORD(wParam) == BN_CLICKED) {
                        nState = ENABLED;
                        CheckRadioButton (hDlg,
                            IDC_ALLOW_BLANK_PASSWORD,
                            IDC_MIN_PASSWORD_LENGTH,
                            IDC_MIN_PASSWORD_LENGTH);
                        
                        // en/disable edit windows
                        EnableWindow (GetDlgItem(hDlg,
                            IDC_PASSWORD_LENGTH_EDIT), TRUE);

                        // if there is no text in both of the edit fields 
                        // then  display information message
                        
                        lPasswordLength = SendDlgItemMessage (hDlg, IDC_PASSWORD_LENGTH_EDIT,
                            WM_GETTEXTLENGTH, 0, 0);
                        if (lPasswordLength == 0) {
                            // no value so use 6 for starters
                            SetDlgItemText (hDlg, IDC_PASSWORD_LENGTH_EDIT,
                                TEXT("6")); 
                            SetFocus (GetDlgItem (hDlg, IDC_PASSWORD_LENGTH_EDIT));
                        }

                        return TRUE;
                    } else {
                        return FALSE;
                    }

                case IDC_ALLOW_BLANK_PASSWORD:
                case IDC_MIN_PASSWORD_LENGTH:
                    // check correct button
                    CheckRadioButton (hDlg,
                        IDC_ALLOW_BLANK_PASSWORD,
                        IDC_MIN_PASSWORD_LENGTH,
                        LOWORD(wParam));
                    // enable or disable the edit window
                    if (LOWORD(wParam) == IDC_MIN_PASSWORD_LENGTH) {
                        nState = ENABLED;
                    } else {
                        nState = DISABLED;
                    }

                    EnableWindow (GetDlgItem(hDlg,
                          IDC_PASSWORD_LENGTH_EDIT), nState);

                    return TRUE;

                case IDC_HELP:
                    PostMessage (GetParent(hDlg), UM_SHOW_CONTEXT_HELP, 0, 0);
                    return TRUE;

                default:
                    return FALSE;
            }

        default:
	        return (FALSE); // Didn't process the message
    }
}

LONG
C2QueryPasswordLength (
    IN  LPARAM   lParam
)
/*++

Routine Description:

    Function called to find out the current state of this configuration
        item. This function reads the current state of the item and
        sets the C2 Compliance flag and the Status string to reflect
        the current value of the configuration item.

Arguments:

    Pointer to the Dll data block passed as an LPARAM.

ReturnValue:

    ERROR_SUCCESS if the function succeeds otherwise a
    WIN32 error is returned if an error occurs

--*/
{
    PC2DLL_DATA pC2Data;
    LONG        lPasswordLength;

    if (lParam != 0) {
        pC2Data = (PC2DLL_DATA)lParam;
        lPasswordLength = GetWorkstationMinPasswordLength();
        if (lPasswordLength > 0) {
            pC2Data->lC2Compliance = SECURE;
            _stprintf (pC2Data->szStatusName,
                GetStringResource (GetDllInstance(), IDS_PASSWORD_NOT_BLANK),
                lPasswordLength);
        } else if (lPasswordLength == 0) {
            pC2Data->lC2Compliance = C2DLL_NOT_SECURE;
            _stprintf (pC2Data->szStatusName,
                GetStringResource (GetDllInstance(), IDS_PASSWORD_CAN_BE_BLANK));
        } else {
            pC2Data->lC2Compliance = C2DLL_NOT_SECURE;
            _stprintf (pC2Data->szStatusName,
                GetStringResource (GetDllInstance(), IDS_UNABLE_READ));
        }
    } else {
        return ERROR_BAD_ARGUMENTS;
    }
    return ERROR_SUCCESS;
}

LONG
C2SetPasswordLength (
    IN  LPARAM   lParam
)
/*++

Routine Description:

    Function called to change the current state of this configuration
        item based on an action code passed in the DLL data block. If
        this function successfully sets the state of the configuration
        item, then the C2 Compliance flag and the Status string to reflect
        the new value of the configuration item.

Arguments:

    Pointer to the Dll data block passed as an LPARAM.

ReturnValue:

    ERROR_SUCCESS if the function succeeds otherwise a
    WIN32 error is returned if an error occurs

--*/
{
    PC2DLL_DATA pC2Data;
    LONG        lPasswordLength = 0;

    if (lParam != 0) {
        pC2Data = (PC2DLL_DATA)lParam;
        if (pC2Data->lActionCode == AC_PW_LENGTH_UPDATE) {
            if (SetWorkstationMinPasswordLength (pC2Data->lActionValue)) {
                lPasswordLength = pC2Data->lActionValue;
                if (lPasswordLength > 0) {
                    pC2Data->lC2Compliance = SECURE;
                    _stprintf (pC2Data->szStatusName,
                        GetStringResource (GetDllInstance(), IDS_PASSWORD_NOT_BLANK),
                        lPasswordLength);
                } else {
                    pC2Data->lC2Compliance = C2DLL_NOT_SECURE;
                    _stprintf (pC2Data->szStatusName,
                        GetStringResource (GetDllInstance(), IDS_PASSWORD_CAN_BE_BLANK));
                }
            } else {
                DisplayDllMessageBox (
                    pC2Data->hWnd,
                    IDS_PASSWORD_ERROR_NO_SET,
                    IDS_PASSWORD_CAPTION,
                    MBOK_EXCLAIM);
            }
            pC2Data->lActionCode = 0;
            pC2Data->lActionValue = 0;
        }
    } else {
        return ERROR_BAD_ARGUMENTS;
    }

    return ERROR_SUCCESS;
}

LONG
C2DisplayPasswordLength (
    IN  LPARAM   lParam
)
/*++

Routine Description:

    Function called to display more information on the configuration
        item and provide the user with the option to change the current
        setting  (if appropriate). If the User "OK's" out of the UI,
        then the action code field in the DLL data block is set to the
        appropriate (and configuration item-specific) action code so the
        "Set" function can be called to perform the desired action. If
        the user Cancels out of the UI, then the Action code field is
        set to 0 (no action) and no action is performed.

Arguments:

    Pointer to the Dll data block passed as an LPARAM.

ReturnValue:

    ERROR_SUCCESS if the function succeeds otherwise a
    WIN32 error is returned if an error occurs

--*/
{
    PC2DLL_DATA pC2Data;
    LONG        lNewValue = 0;

    if (lParam != 0) {
        pC2Data = (PC2DLL_DATA)lParam;
        if (DialogBoxParam (
            GetDllInstance(),
            MAKEINTRESOURCE (IDD_PASSWORD_LENGTH),
            pC2Data->hWnd,
            C2PasswordLengthDlgProc,
            (LPARAM)&lNewValue) == IDOK) {
            pC2Data->lActionValue = lNewValue;
            pC2Data->lActionCode = AC_PW_LENGTH_UPDATE;
        } else {
            // no action
            pC2Data->lActionCode = AC_PW_LENGTH_NOCHANGE;
        }
    } else {
        return ERROR_BAD_ARGUMENTS;
    }

    return ERROR_SUCCESS;
}