Friday, June 27, 2008

Preventing accidental NTFS data moves

This post tries to deal with the eternal problem of users accidentally moving data around on an NTFS volume just because they can, describing my understanding of the problem and the lack of a solution with NTFS permissions only, and a method I've used to work around this problem.

This problem is most apparent with a single share containing top-level directories with different security. When a user has change control to more than one directory, it then becomes possible to drag and drop whole top-level folders into other folders.

When this occurs on the same NTFS volume, it seems the file MoveFileEx function is smart enough to re-link the object to a new parent in the MFT FRS entry for the directory, rather than a recursive copy/delete operation. This is very efficient if it's what you're expecting, but the less than intuitive impacts of this include:

  1. Permissions on child objects - subfolders or files - are ignored in the re-link move, including lack of permissions and specific access denied ACLs
  2. The ACL on the source directory is not reset when it gets to the target, including inheritance from the new parent, and inheritance that was valid in the old parent

For example:
Share\A - Ausers:C
Share\B - Busers:C
Share\B\File.txt - Busers:R
Share\B\Data - Busers:C (inherited from the parent B)

  1. A user that's in AUsers and BUsers accidentally drags the B directory into A. If the destination A\B directory doesn't exist and the user has the delete right to B, the file will be re-linked in NTFS, totally ignoring the fact that the user only has read-only access to B\file.txt.
  2. Instead of dragging the whole top-level directory, the user drags B\Data into A. Again, if A\Data doesn't exist and the user has Delete to Data, the directory is re-linked in NTFS. Looking at the permissions of the new A\Data, it still lists an ACE of BUsers:C, inherited from the 'parent object' that is obviously no longer the parent.

There are many ways of dealing with this problem, for example, you could:

  • Remove change control and use Write. This would be very simple security to manage, but this would prevent users from deleting/renaming files and subdirectories. If creator owner:C were added, this would allow users to delete/rename their own data, but not move/delete/rename data that already exists. This is probably a better solution and would prevent accidental moves/deletes of any kind by normal users, but requires a lot more effort to manage.
  • A small group of custodians could be responsible for managing the creation and deletion of directories, reducing the risk by removing the right to delete from most users.
  • Prevent drag-and-drop through explorer on workstations.
  • Develop a filesystem mini-filter that sits at an altitude to interpret file system operations that are the result of a drag and drop request, and deny requests that involve too much change (or the top 3 levels of each top-level directory for example)
  • Develop a WH_GETMESSAGE hook to intercept explorer drag-and-drop messages and cancel them before the request gets to the server
  • Develop a DropHandler for Directory/Folder objects to filter requests.

However, these solutions generally require too much effort, so I've come up with the following relatively simple workaround:

Prevent a move operation completed as a copy/delete on top-level folders by:

  • Creating a placeholder file within each top-level directory, with users having read-only access to the file. This file will be processed first due to the name beginning with a space (0x20 – processed first in tests), and explorer will immediately return an access denied message. The file should have the hidden attribute set, eg ‘ placeholder.txt’

Prevent users from performing NTFS re-link moves within a volume on top-level directories by:

  • Removing Delete from the top-level directory - part of Change, which general practice is to give users - typically this folder, subfolders and files. As part of a move (drag/drop, cut/paste), if users have the Delete right to the source directory object and a same-named target folder doesn't already exist, NTFS will re-link the directory to the new parent regardless of permissions on the source subfolders and files. This could be achieved by using C: OICIIO (object-inherit, container-inherit, inherit-only), and RWX to the top-level directory, ensuring that a recursive copy/delete operation is performed, which does check access control, and re-inherit permissions in the target.

For example, a user has access to both A and B, with the placeholders secured for read-only:
Share - Users:R
Share\A - AUsers:C
Share\A\placeholder.txt - AUsers:R
Share\B - BUsers:C
Share\B\ placeholder.txt - BUsers:R

In the example above, these changes will prevent the user from:

  • Deleting an entire directory, either A or B, prevented by the placeholder file (deleting the contents) and the lack of Delete on the container.
  • An accidental drag-and-drop of B into A, made into a copy/delete operation by the lack of Delete on the container and prevented as a copy/delete by the placeholder file which is processed first. Note that A\B folder would still be created with inherited permissions of A, but no contents would be copied/deleted.
  • Renaming either A or B. Users only have read on the root, delete is required to rename.

Under normal circumstances with drag and drop in explorer from XP workstation to a 2003 file server, if the following is true then the move operation will re-link the top-level directory within NTFS by attaching it to a new parent, as opposed to a copy/delete operation:

  1. If the data is on the same volume, presented to the user through a share, with or without Access Based Enumeration
  2. If the user has the delete right to the directory object that is the source of the drag operation.
  3. If in the drop target, a folder does not already exist with the same name.

In this scenario, access control is not validated on child objects within the drag source and permissions are not reset in the new drop target (inherited or direct).


  1. The user must have access to read the placeholder when using Access Based Enumeration, otherwise the file will simply be hidden and all other objects will be moved (as a copy/delete)
  2. Testing with a re-link move operation and a copy/delete move operation was completed, using diskedit.exe to find the File Record Segment number for the file from the NTFS MFT. When copy/delete was used, a new target directory was created with a new MFT entry, whereas when the object was re-linked, the FRS number remained the same, and the FILE_REFERENCE ParentDirectory entry in the $FILE_NAME attribute was updated to reflect the new parent.
  3. If an object in the drag source is locked by another user (eg command prompt chdir to a subfolder on the console of the server), and in the scenario where the folder would normally be moved at the top-level (instead of copy/delete), explorer on the workstation will automatically fall-back to the copy/delete method).
  4. The same occurs on the console of the file server managing the local volume, moving folders is changing the parent object at a MFT/FRS level, nothing to do with access control on the objects (assuming Delete on the source and create directory on the target)
  5. Using the MoveSecurityAttributes registry value (310316) on the server does ensure that permissions are not copied, which does inherit new permission in the target. However, this can also be confusing, as moving and then moving back would lose permissions.
  6. To determine processing order, several test directories and files were created, and testing shows that directories are processed last-first, ie ASCII character 126 (0x7e) ‘~’ is processed first for directory moves. However, files within a directory are ‘moved’ before directories, and files are processed first-last, and 32 (0x20) is the first common printable character.
  7. Preferably secedit security templates would be used to control the security on the filesystem, providing a repeatable method to apply security.


Inherited permissions are not automatically updated when you move folders

MoveFileEx Function

How NTFS Works

How to configure file sharing in Windows XP

How permissions are handled when you copy and move files and folders

When you try to move files from one network drive to another network drive, the files keep permissions from the source folders on a client computer that is running Windows XP or Windows Server 2003

Viewing NTFS information with nfi and diskedit

Wayne's World of IT (WWoIT), Copyright 2008 Wayne Martin.

Read more!

Sunday, June 22, 2008

FSRM and NTFS Quotas in 2003 R2

This post discusses several methods of using File Server Resource Manager (FSRM) auto-quotas with a single share for many home directories, and how you can bypass the limitation with FSRM quotas over SMB and return a reduced amount of disk space through the single share. The two methods discussed are reparse points, and combined FSRM and NTFS quotas.

There is an inherent problem with FSRM quotas in Windows Server 2003 R2 – when accessed remotely, a hard quota is used to report disk free space to the client only when a quota is set on the root of the disk or share. The share overwrites volume root if both have hard quotas set.

Unfortunately this is not practical in this scenario, as the free space from the quota root down will be affected by a hard quota. For example, a hard quota set on the root of the share, where that share contains user home directories, the total space would be limited based on the quota, rather than limiting each home directory. No method could be found to prevent inheritance of a quota setting to sub-folders.

Note that this does not occur when accessing the quota locally on a machine; the problem exists due to the SMB call for QUERY_FS_INFO is querying the free space at the root, not the free space at the folder (historically there was no difference). File screening has the capability to include a blocking exception entry deeper in the tree to override policies above, but quotas do not have the same interface through the GUI.

The following methods were tried (and failed) to see if there was an easy workaround for this issue:

However, if this functionality is required, there are at least two methods to work around the problem – using reparse points or using a combination of NTFS quotas and FSRM quotas.

Reparse Points

Testing was conducted to see whether reparse points, junctions, mount points or symbolic links could be used to return a different amount of free space from the root of the volume compared to the quota applied to each home drive folder.

Using one directory junction, one share, one hard quota and one autoquota, it is possible to use FSRM R2 quotas to report the free disk space based on a hard quota at a root folder, while still providing different per-folder quotas.

For example, in the following scenario, it’s possible to report a reduced disk free space limit, using only FSRM quotas and a directory junction point on the same volume.

  1. Cluster share Root: f:\QuotaTest - file://server/QuotaTest
  2. User Home Root: file://server/f$/users
  3. User home drive: \\server\quotatest\junction\user1 (f:)
  4. FSRM Hard Quota on the share root: 10MB
  5. FSRM Hard or Soft autoquota on the home directory root: 20MB
  6. Junction Directory: f:\quotatest\junction
  7. Junction Target: f:\users
  8. Create the directory junction/reparse point: junction.exe f:\quotatest\junction f:\users

Tests completed under this scenario from a workstation:

  1. Directory of H: on reports 10MB free space, based on the hard quota set at the root of the share
  2. Explorer view of H: reports 10MB free space, with the drive mapped through the junction (AD)
  3. Copy a 13MB file to H: succeeds, still 10MB reported free, FSRM warning triggered based on 50% usage (of the 20MB)
  4. Copy another 13MB file to H: fails, 20MB hard autoquota set on h:\users prevents copy


  1. Apparently Windows Vista clients using SMB 2.0 do not have this issue
  2. Windows 2000 and later support directory junctions – reparse points. When accessing a reparse point, the processing occurs on the server, unlike Vista/2008 which has a modified MUP and network redirector architecture, supporting client-side processing of file and directory symbolic links.
  3. This still has at least one major disadvantage in that free space will not change for users, they would always see the free space available at the root of the share, 10MB in the example above. However, if hard FSRM autoquotas were used without this method, the free space reported to users would be the total free space on the volume, regardless of the 10MB hard limit that they would be limited to. This is potentially confusing in both scenarios.

Combined FSRM and NTFS quotas

Being completely different technologies, it doesn’t seem that NTFS quotas and FSRM quotas conflict with each other. Therefore one method of providing soft/hard FSRM quotas and also reducing the disk space seen by users is to also use NTFS hard quotas.

There are several caveats with this approach:

  1. NTFS quotas are only relevant for user-owned data, where each user has data in one directory, ideal for home directories, but not suitable for shared data directories.
  2. The two different quota systems would have to be separately maintained and aligned as configuration changes in the other. While all users conform to the standard template this would not be challenging, but as individual quotas are changed this will become problematic (as always happens).

Overall this solution provides a more realistic disk-free result for each user, provided the FSRM hard quota matches the NTFS hard quota, and file ownership is correctly set.

The following testing was completed with FSRM and NTFS quotas working together in a 2003 MSCS cluster:

  1. Hard NTFS quota of 15MB
  2. Soft auto-quota of 20MB
  3. Writing a file using user1 to the H: drive, automatically creates a quota entry in NTFS quotas
  4. Writing a second file which takes it over 10MB (50%), the FSRM quota event/command takes place
  5. The user doing a directory of the filesystem reports only the NTFS hard quota disk free space.
  6. Trying to copy another file as user1 to the H: drive fails with not enough disk space according to the hard NTFS quota
  7. Moved the cluster group to verify this follows on a cluster
  8. After the group was moved to another server, conducted same tests, NTFS quotas still apply and hard limites being returned to the client as total space.

Wayne's World of IT (WWoIT), Copyright 2008 Wayne Martin.

Read more!

PowerShell Deleting NTFS Alternate Data Streams

This post provides various command-line methods of creating, referencing, extracting and deleting NTFS alternate data streams. The PowerShell script was originally intended to delete NTFS attributes other than $data, but I don't think this is possible using DeleteFile from Kernel32.

The PowerShell script is really just a wrapper around a simple call to DeleteFile in VB.Net, using the syntax %file%:%stream%:%Attribute, eg test.txt:stream1:$DATA

Included below the script are methods to create streams using echo and type in files and directory objects, run executable code from an alternate stream, and display detail on the streams using nfi.exe and streams.exe.


#-- DeleteNTFSStream.ps1

   [string] $filename,
   [string] $attribute,
   [string] $stream

# Description:
#  Delete an NTFS alternate data stream for the specified file
# Author: 
#  Wayne Martin, 22/06/2008,
# Usage
#  powershell . .\DeleteNTFSStream.ps1 -f "d:\temp\test.txt" -a "`$Data" -s "stream1"
# References:
#  Accessing alternative data-streams of files on an NTFS volume

$provider = new-object Microsoft.VisualBasic.VBCodeProvider
$params = new-object System.CodeDom.Compiler.CompilerParameters
$params.GenerateInMemory = $True
$refs = "System.dll","Microsoft.VisualBasic.dll"

$VBCode = @'
Imports System

Class DeleteNTFSStream

    '''Return Type: BOOL->int
    '''lpFileName: LPCWSTR->WCHAR*
    Public Shared Function DeleteFileW( ByVal lpFileName As String) As  Boolean
    End Function

    Sub main(ByVal lpFileName As String, ByVal lpAttributeName As String, Optional ByVal lpStreamName As String = "")

        Dim lpFile As String = ""
        lpFile = lpFileName + ":" + lpStreamName + ":" + lpAttributeName
        If (lpFile <> "") Then
            console.writeline("Deleting " + lpFile)
        End If
    End Sub
End class

$cr = $provider.CompileAssemblyFromSource($params, $VBCode)
if ($cr.Errors.Count) {
    $codeLines = $VBCode.Split("`n");
    foreach ($ce in $cr.Errors)
        write-host "Error: $($codeLines[$($ce.Line - 1)])"
        write-host $ce
        #$ce out-default
    Throw "INVALID DATA: Errors encountered while compiling code"
$mAssembly = $cr.CompiledAssembly
$instance = $mAssembly.CreateInstance("DeleteNTFSStream")

$result = $instance.main($filename, $attribute, $stream)
write-host $result


Add a string to a stream $Data attribute:
echo this content will be stored in the file sub stream > test.txt:stream1

Add the contents of a file to an alternate data strem
type file.txt > test.txt:stream1

Create a new file in an alternate stream as part of a directory:
md test & echo alternate stream > test:stream1

Display the stream:
more < test.txt:stream1

Display the NTFS attributes for the file
nfi c:\temp\test.txt
NTFS File Sector Information Utility.
Copyright (C) Microsoft Corporation 1999. All rights reserved.

$FILE_NAME (resident)
$DATA (resident)
$DATA stream1 (resident)

Add an executable as an alternate stream (the stream is attached to a directory in this case)
type c:\windows\system32\notepad.exe > test:test.exe

Run the executable in the alternate stream
cmd /c start .\test:test.exe

View the directory and its alternate stream containing notepad.exe
nfi c:\temp\test
$FILE_NAME (resident)
$DATA test.exe (nonresident)
logical sectors 659144-659151 (0xa0ec8-0xa0ecf)
logical sectors 660312-660319 (0xa1358-0xa135f)
logical sectors 646776-646791 (0x9de78-0x9de87)
logical sectors 701064-701095 (0xab288-0xab2a7)
logical sectors 5101080-5101143 (0x4dd618-0x4dd657)
logical sectors 27656944-27656951 (0x1a602f0-0x1a602f7)
$INDEX_ROOT $I30 (resident)
$INDEX_ALLOCATION $I30 (nonresident)
logical sectors 11436456-11436463 (0xae81a8-0xae81af)
$BITMAP $I30 (resident)

Retrieve the binary file from the stream and store in the default stream of a new file (using win32 port of Unix Cat)
cat test:test.exe > note.exe

Remove a stream (does not maintain timestamps)
type test.txt>test1.txt

Delete an alternate data stream
streams -d test.txt


nfi.exe, part of OEM Support Tools Phase 3 Service Release 2 Availability

Streams v1.56

Accessing alternative data-streams of files on an NTFS volume

Find and delete NTFS Alternate Data Streams (ADS)

How To Use NTFS Alternate Data Streams

Practical Guide to Alternative Data Streams in NTFS

Named Attributes

Viewing NTFS information with nfi and diskedit

Wayne's World of IT (WWoIT), Copyright 2008 Wayne Martin.

Read more!

Thursday, June 19, 2008

NTFS links - reparse, symbolic, hard, junction

Reading through various Microsoft documents, it seems several terms have been adopted to describe methods of linking one NTFS object to another in different scenarios. I tried and couldn't find a single description of the different methods; therefore below you'll find my interpretation, mostly describing how they link to each other, and the tools available to manage the links.

  • Reparse Point File - user-defined data, interpreted by a file sytem filter, eg. RIS SIS, Microsoft RSS
  • Reparse Point Directory - Map a local folder to any other local folder on or across local volumes, eg c:\windows\temp mapped to c:\temp
  • Hard Link File - filesystem link linking one file to another, linking a file object to one or more directory entries.
  • NTFS Junction Point Directory -> Reparse Point Directory
  • Directory Symbolic Link -> NTFS Junction Point Directory
  • Volume Mount point -> Directory Symbolic Link mapping the root of one local volume to a folder in another local volume
  • Soft Link Directory -> NTFS Junction Point Directory -> Reparse Point Directory
  • NTFS Junction Point Files -> Reparse Point File
  • Symbolic Directory Links -> NTFS Junction Point Directory
  • Symbolic File Links Matching Unix symbolic link (soft link) functionality with absolute file, relative file in local -> local, local -> remote, remote -> local and remote -> remote combinations, available in Vista and 2008.

Tools to manage links:

  • linkd.exe (2000 resource kit) - Create NTFS Junction Points (the source directory must be empty)
  • mountvol.exe (2000 CD-rom) - Manage directory symbolic links deisnged to mount a volume (which may or may not have a drive letter already associated) bypassing 26 drive letter limitations
  • delrp.exe (2000 resource kit) - Delete NTFS junction points and other types of reparse points
  • junction.exe (sysinternals) - Create or delete NTFS Junction points (reparse points). Note that you can create a junction that maps a directory to a file target, it just doesn't achieve much.
  • fsutil.exe hardlink (oem with XP/2003) - Create hard file links
  • fsutil.exe reparsepoint (oem with XP/2003) - Query or delete reparse points
  • mklink - Vista/2008 utility to create symbolic directory and file links, hard links and directory junctions


Symbolic links:

Getting the free space available under a certain directory

Creating Symbolic Links

Junction utility:

Creating Symbolic Links

How to create and manipulate NTFS junction points

Reparse Point Support in Windows 2000-Based Clusters

Hard Links and Junctions

Reparse Points

Symbolic Linking

Reparse Point Support in Windows 2000-Based Clusters

How to create and manipulate NTFS junction points

Fsutil: hardlink

File-Based Symbolic Links

Wayne's World of IT (WWoIT), Copyright 2008 Wayne Martin.

Read more!

Tuesday, June 17, 2008

IE Warnings when files are executed

In some scenarios, a prompt will occur when trying to download and run an executable either through Internet Explorer or VBScript from a FQDN UNC path. I've had this occur to me when a VBScript was trying to execute robocopy.exe from a remote share using a fully qualified domain name UNC path. The script was not running on the interactive desktop, and the prompt asking for permission to allow execution prevented the script from finishing.

Windows XPSP2 and Windows Server 2003 SP1 both have new functionality with downloaded files that may be executed and check the digital signature on the files. If the binary does not contain a digital signature, a 'Open File - Security Warning' popup indicating that the publisher could not be verified will be displayed, awaiting user interaction to allow or deny the execution request.

In the VBScript scenario, this was only happening because the execution was called from a Fully Qualified Domain Name, and despite being the local domain (in this case), it was still interpreted as a threat and a warning was presented.

-- Popup
Open File - Security Warning
The publisher could not be verified. Are you sure you want to run this software

Name: Robocopy.exe
Publisher: Unknown publisher
Type: Application
From: FQDN server
This file does not have a valid digital signature that verifies its publisher.


Add HKU\.Default\Software\Microsoft\Windows\CurrentVersion\Policies\Associations\LowRiskFileTypes and ensure a semi-colon separated list of extension types exist with those you want to allow. Eg '.exe;.cab'

Note that the path above is to the .default hive, used by the System account. Add to 'Default User\ntuser.dat' or HKCU to modify the default or change the current user respectively. Group policy could also be used to control this setting.

Duplicating the problem:

You can verify the problem before and after by pasting a FQDN UNC path to an IE window, eg

Or by running the following VBScript:

Set objShell = CreateObject("WScript.Shell")
strCMD = "\\\c$\windows\system32\robocopy.exe"
= objShell.Run(strCMD, 0, TRUE)

Note that robocopy.exe version XP010 was used in these tests, running on an XP SP2 workstation to a 2003 server.



Detailed Information on IE problems with XPSP2/2K3SP1:

Description of IE security zone registry entries:;en-us;182569

Problems adding top-level domains to zones site list

Wayne's World of IT (WWoIT), Copyright 2008 Wayne Martin.

Read more!

Sunday, June 15, 2008

PowerShell Low-level keyboard hook

This post provides an example PowerShell script to create a global system low-level keyboard hook, detecting and discarding the first Alt+Tab combination after the hook is installed. This was developed and tested on Windows Vista.

This is run out of a PowerShell script using runtime compiled VB.Net code, with a form-less Application.Run() call to wait for the message. You could also hook WH_MOUSE_LL using similar code.

I was originally trying to hook WH_GETMESSAGE, but it seems this is not possible without a DLL which is loaded and injected into running processes to provide the shared address space, something which negated what I was trying to achieve (a simple demo with PowerShell).

Note that the MSDN SetWindowsHookEx Function description ( defines WH_KEYBOARD_LL as a global only hook, which to my understanding means it needs to be referenced in a DLL, which is most definitely not the case below as hMod is and it's all running out of script/dynamic code. I also tested using the hinstance of the compiled module, which didn't work, and using loadlibrary user32 which did work (I don't understand why I just copied from some other example).


## KeyboardHookExample.ps1 ##

# Description:
#  Example global WH_KEYBOARD_LL hook in PowerShell using VB.Net runtime compiled code.
#  Adds a hook in the LIFO chain for all keyboard activity to CallbackFunction(), which checks for and discards the first Alt+Tab combination
# Author: 
#  Wayne Martin, 15/06/2008,
# References:
#  CLRInsideOut\WinSigGen.exe

$provider = new-object Microsoft.VisualBasic.VBCodeProvider
$params = new-object System.CodeDom.Compiler.CompilerParameters
$params.GenerateInMemory = $True
$refs = "System.dll","Microsoft.VisualBasic.dll","System.Windows.Forms.dll"

$txtCode = @'

Imports Microsoft.VisualBasic
Imports System
Imports System.Runtime.InteropServices
Imports System.Windows.Forms

Public Class hookExample
    Public hookHandle As Integer
    Public hWndExplorer as Integer

     Private CallbackDelegate As HookProc

    Public Structure KBDLLHOOKSTRUCT
        '''DWORD->unsigned int
        Public vkCode As UInteger
        '''DWORD->unsigned int
        Public scanCode As UInteger
        '''DWORD->unsigned int
        Public flags As UInteger
        '''DWORD->unsigned int
        Public time As UInteger
        '''ULONG_PTR->unsigned int
        Public dwExtraInfo As UInteger
    End Structure

    '''Return Type: LRESULT->LONG_PTR->int
    '''code: int
    '''wParam: WPARAM->UINT_PTR->unsigned int
    '''lParam: LPARAM->LONG_PTR->int
    Public Delegate Function HOOKPROC(ByVal code As Integer, ByVal wParam As System.IntPtr, ByVal lParam As System.IntPtr) As Integer

    Public Structure HHOOK__
        Public unused As Integer
    End Structure

    Public Structure HINSTANCE__
        Public unused As Integer
    End Structure

    '''Return Type: HHOOK->HHOOK__*
    '''idHook: int
    '''lpfn: HOOKPROC
    '''hmod: HINSTANCE->HINSTANCE__*
    '''dwThreadId: DWORD->unsigned int
    Public Shared Function SetWindowsHookExW(ByVal idHook As Integer, ByVal lpfn As HOOKPROC,  ByVal hmod As System.IntPtr, ByVal dwThreadId As UInteger) As System.IntPtr
    End Function

    '''Return Type: LRESULT->LONG_PTR->int
    '''hhk: HHOOK->HHOOK__*
    '''nCode: int
    '''wParam: WPARAM->UINT_PTR->unsigned int
    '''lParam: LPARAM->LONG_PTR->int
    Public Shared Function CallNextHookEx( ByVal hhk As System.IntPtr, ByVal nCode As Integer, ByVal wParam As System.IntPtr, ByVal lParam As System.IntPtr) As Integer
    End Function

    '''Return Type: BOOL->int
    '''hhk: HHOOK->HHOOK__*
    Public Shared Function UnhookWindowsHookEx( ByVal hhk As System.IntPtr) As  Boolean
    End Function

    '''HC_ACTION -> 0
    Public Const HC_ACTION As Integer = 0

    Public Const LLKHF_ALTDOWN As Integer = (KF_ALTDOWN) >> (8)
    '''KF_ALTDOWN -> 0x2000
    Public Const KF_ALTDOWN As Integer = 8192

    '''VK_TAB -> 0x09
    Public Const VK_TAB As Integer = 9

    '''WH_KEYBOARD_LL -> 13
    Public Const WH_KEYBOARD_LL As Integer = 13

    '''lpLibFileName: LPCWSTR->WCHAR*
    Public Shared Function LoadLibraryW( ByVal lpLibFileName As String) As System.IntPtr
    End Function

    Public Sub New()
        CallbackDelegate = New HookProc(AddressOf CallbackFunction)

        'Dim hInstance As IntPtr
        ' This doesn't work:
        'hInstance = Marshal.GetHINSTANCE(Reflection.Assembly.GetExecutingAssembly().GetModules()(0))     
        'hookHandle = SetWindowsHookExW(WH_KEYBOARD_LL, CallbackDelegate, hInstance, 0)

        ' This does work, but is unnecessary and also works:
        'hInstance = LoadLibraryW("User32")
        'hookHandle = SetWindowsHookExW(WH_KEYBOARD_LL, CallbackDelegate, hInstance, 0)

       hookHandle = SetWindowsHookExW(WH_KEYBOARD_LL, CallbackDelegate, IntPtr.Zero, 0)
        If (hookHandle > 0) Then
            console.writeline("Keyboard hooked, press Alt+Tab when any window has focus to test")
        End If
    End Sub

    Public Function CallbackFunction(ByVal code As Integer, ByVal wParam As IntPtr, ByVal lParam As IntPtr) As Integer
        Dim HookStruct As KBDLLHOOKSTRUCT
        If (code < 0) Then
            Return CallNextHookEx(hookHandle, code, wParam, lParam)
        End If

        hookStruct = CType(Marshal.PtrToStructure(lParam, GetType(KBDLLHOOKSTRUCT)),KBDLLHOOKSTRUCT)

        If (Hookstruct.vkCode = VK_TAB) And (Hookstruct.flags And LLKHF_ALTDOWN) Then
            console.writeline("Alt+Tab detected, discarding and removing hook")
            Call UnhookWindowsHookEx(hookHandle)
            Return 1
            Return CallNextHookEx(hookHandle, code, wParam, lParam)
        End If
    End Function

End Class


$results = $provider.CompileAssemblyFromSource($params, $txtCode)

if ($results.Errors.Count) {
    $codeLines = $txtCode.Split("`n");
    foreach ($ce in $results.Errors)
        write-host "Error: $($codeLines[$($ce.Line - 1)])"
        write-host $ce
        #$ce out-default
    Throw "INVALID DATA: Errors encountered while compiling code"

$mAssembly = $results.CompiledAssembly
$i = $mAssembly.CreateInstance("hookExample")
#$r = $i.New()

Wayne's World of IT (WWoIT), Copyright 2008 Wayne Martin.

Read more!

Wednesday, June 11, 2008

Cross-forest authentication and GP processing

This post describes the use of UDP versus TCP Kerberos in cross-forest access, Kerberos logging, network requirements, cross-forest user-based group policy processing and authentication protocols. I have used this information to provide firewall rules between forests, diagnose Kerberos issues and just generally have a better understanding of what's happening with cross-forest access in an enterprise environment.

Cross-forest management

Depending on whether a standard authentication request has been made, or whether an administrative function is performed can have an effect on whether Kerberos over TCP or Kerberos over UDP is used.

Using the standard GUI object picker, setting up trust relationships, or management scripting using ADSI/LDAP makes use of Kerberos UDP Port 88, whereas an actual authentication request - an interactive user logon - will use Kerberos TCP Port 88.

This is an important distinction when setting up firewall rules, although Kerberos can be modified to use only TCP if required.

Kerberos Logging and utilities

Turn on detailed Kerberos logging (see the 'Troubleshooting Kerberos Errors' reference for more information).

Useful utilities:
- Kerbtray, klist - show/purge tickets and TGT information
- tokensz - Show the token size, useful for determining if the Privilege Attribute Certificate (PAC) data in the TGT will be over-sized, causing UDP transmission to fail.

Network Ports for Kerberos

Port requirements for Kerberos:




DNS serviceThe internal DNS server needs to be accessible to all clients for the location of KDC computers. The Active Directory domain controllers need to be able to access external DNS servers for resolving external domain name requests.



Kerberos ticket-granting serviceAll clients need to be able to connect to this port on the KDC servers.



Time serviceAll clients need to be able to connect to this port for time synchronization, either to an internal time server or to an external time source. The internal time server will need to connect to an external time source to synchronize.
464/TCPMicrosoft Windows 2000 Kerberos change password protocolThis port is also used by the kpasswd protocol. This port should only be open if clients use the kpasswd protocol.

Cross-domain Group Policy processing

When logging on to a computer in another domain with an account in a trusted domain, group policy is not applied by default. To change this behaviour, the 'Allow Cross-Forest User Policy and Roaming Profiles' policy setting must be set to enabled. If enabled, user policy will be applied from the user's forest, and roaming profiles will be available if configured and accessible.

Once cross-forest policy processing has been enabled, accessing that cross-forest Group Policy has the following requirements:

  • SMB/NetBIOS over TCP/IP access to a Domain Controller in the users's domain from the machine being logged on to. This is not necessarily a DC in the computer domain, and could be a member server or workstation.
  • LDAP over TCP from the local trusting Domain Controller (the computer account domain) to a trusted Domain Controller in the user forest

Cross-domain authentication

Many different scenarios exist for authenticating across domains and forests, depending on the request type, the domain trusts, the trust types, the OS types, name resolution and Active Directory domain levels.

In a default Windows 2000 or Windows Server 2003 domain, Kerberos is usually the protocol of choice. When Kerberos is unavailable, for example due to Network access or time synchronisation issues, NTLM/NTLMv2 will be used. Each protocol has different requirements.

Excerpt from the references below:

In an Active Directory environment the Kerberos-based authentication process is most commonly used. To access a shared resource in another domain by using Kerberos authentication, a computer where the user logs on first requests a ticket from a domain controller in its account domain to the server in the trusting domain that hosts the requested resource. This ticket is then issued by an intermediary trusted by both the requesting computer and the server. The computer then presents this trusted ticket to the server in the trusting domain for authentication. This process, however, becomes more complex when a workstation in one forest attempts to access data on a resource computer in another forest.

In this case, the Kerberos authentication process contacts the domain controller for a service ticket to the SPN of the resource computer. Once the domain controller queries the global catalog and determines that the SPN is not in the same forest as the domain controller, the domain controller sends a referral for its parent domain back to the workstation. At that point, the workstation queries the parent domain for the service ticket and continues to follow the referral chain until it reaches the domain where the resource is located. For more detailed information about how authentication requests are processed across domains and forests, see 'How Domain and Forest Trusts Work.'


Documentation on domains and forests, including Kerberos and NTLM

How to configure Windows 2003 SP1 firewall for a Domain Controller;en-us;555381&sd=rss&spid=3198

Domain and Forest Trust Tools and Settings

How to force Kerberos to use TCP instead of UDP in Windows Server 2003, in Windows XP, and in Windows 2000;en-us;244474

Active Directory Replication over Firewalls

Troubleshooting Kerberos Errors

Download token size

Wayne's World of IT (WWoIT), Copyright 2008 Wayne Martin.

Read more!

Deleting Invalid SMS 2003 Distribution Points

If a server is decommissioned before removing SMS, orphaned Distribution Points are left in the SMS 2003 database. This becomes apparent in the SMS Administrator Console when trying to add a package to a Distribution Point, showing blank DP entries.

This post provides information on methods to cleanse the SMS database, with all methods going through the SMS provider.

Method 1 - Use the CleanDP.vbs VBScript

cscript CleanDP.vbs %server%

Method 2 - Use WMIC

Query for the distribution points for the server:

wmic /namespace:\\root\sms\site_%sitecode% /node:%server% path sms_distributionpoint WHERE "ServerNALPath like '%serverToDelete%'" get *

And then execute the delete method against the distribution point:

wmic /namespace:\\root\sms\site_%sitecode% server% path sms_distributionpoint WHERE "ServerNALPath like '%serverToDelete%'" delete

Note that the delete command can be quite powerful, use the /interactive:on global switch to prompt for each deletion.

Method 3 - Use the WMI CIM Studio

The Microsoft WMI CIM Studio application provides a GUI interface for WMI management, allowing connections to servers and paths, along with executing WQL queries and providing the possibility of deleting the result set.

' -- CleanDP.vbs
' Update strSiteServer and strSiteCode, and uncomment the objDP.Delete_ line
strSiteServer = "SERVER"
strSiteCode = "AAA"

If WScript.Arguments.UnNamed.Count = 1 Then
strServer = WScript.Arguments.UnNamed(0)
WScript.Echo "Provide a server to delete all the packages from"
End If

wscript.echo strserver

Set objNameSpace = GetObject("winmgmts:" & "\\" & strSiteServer & "\root\sms\site_" & strSiteCode)

strQuery = "SELECT * " & _
"FROM SMS_DistributionPoint " & _
"WHERE ServerNalPath Like '%" & strServer & "%'"

Set objDPSet = objNameSpace.ExecQuery(strQuery)
For each objDP in objDPSet
wscript.echo objDP.PackageID & ", " & objDP.SiteCode & ", " & objDP.ServerNALPath & ", " & objDP.Status


Wayne's World of IT (WWoIT), Copyright 2008 Wayne Martin.

Read more!

Monday, June 9, 2008

Cross-forest authentication and site synchronization

This post describes a few scenarios with cross-forest trusts, and in particular the authentication protocols and DC locator processes that can occur with NTLM trusts between 2000 and 2003 forests. The goal of this post is to provide background information on how this works, and how the process can be improved in an enterprise environment with cross-forest site synchronization.

For the information below, the following test network was used:
- resource.local - resource domain - Windows Server 2003 forest, one domain, RL1 site
- - user domain - Windows 2000 Server forest, one domain, UC1 siet
- resource.local trusts, with an external one-way NTLM trust

Protocols In Use

The following authentication protocols are typically used in a Windows 2000 and Windows 2003 domain environment:

  • NTLM - Protocol negotiation and authentication typically results in NTLM when accessing cross-forest resources across NTLM trusts. For example, a workstaiton and user in accessing a resource on a member server in resource.local
  • Kerberos - An Interactive logon from a trusted 2000 domain to a trusting 2003 domain across forests can still result is cross-forest Kerberos tickets. For example, an account in the trusted 2000 user domain logging on to the trusting resource forest using terminal services results in tickets for both TGT (Ticket Granting Tickets) for the user forest and LDAP tickets for DC access.


  1. Kerberos is not documented as normal for cross-forest authentication except with Windows Server 2003 cross-forest trusts. Testing shows that interactive logons using cross-forest accounts across NTLM trusts can still use Kerberos.
  2. When using NetBIOS names - the method primarily used within NTLM trusts - the NT4 compatible locator is used, making use of transport-specific mechanisms, mailslots and NETLOGON_SAM_LOGON_REQUEST and responses to locate a Domain Controller and authenticate the request.
  3. To understand the Domain Controller locator process, it is important to acknowledge that even though the request may be originating from a client in another domain sent directly to a member server in the resource domain, this resource server then becomes the 'client', using Netlogon RPC calls with its local Domain Controller, submitting and/or proxying the request from the workstation
  4. Netlogon debugging can aid the diagnosis of issues with logon from the perspective of clients, Domain Controllers, or both. Nltest can be used to set the level of logging, ranging from 0x0 (no logging) to 0xffffffff (full logging). When using full logging, it is possible to see the detailed (unencrypted) NETLOGON_SAM_LOGON_RESPONSE, showing the domain/forest name, the chosen DC, the site of both the DC and the client (if set).

Query for a DC

For example, the following shows the conversation from the Netlogon perspective of a client workstation in the domain and a DC in the resource.local domain when the workstation queries for a Domain Controller:

Workstation in the user domain:

03/20 07:16:27 [MISC] DsGetDcName function called: Dom:resource.local Acct:(null) Flags: FORCE DSP
03/20 07:16:27 [DNS] Cache: RESOURCE resource.local: Found existing domain cache entry
03/20 07:16:27 [MAILSLOT] NetpDcPingListIp: resource.local: Sent UDP ping to
03/20 07:16:27 [MAILSLOT] resource.local: Received 'Sam Logon Response Ex' response.
03/20 07:16:27 [DNS] Cache: RESOURCE resource.local: Ditch existing cache entry 1 (Quality: 13)
03/20 07:16:27 [DNS] Cache: RESOURCE resource.local: Add cache entry 1 (Quality: 13)
03/20 07:16:27 [MISC] DsGetDcName function returns 0: Dom:resource.local Acct:(null) Flags: FORCE DSP

Domain Controller in the resource forest - resource.local

03/20 07:16:27 [MAILSLOT] Received ping from WS01 resource.local (null) on UDP LDAP
03/20 07:16:27 [SITE] 192: Lookup: Doing byte
03/20 07:16:27 [SITE] 168: Lookup: Doing byte
03/20 07:16:27 [SITE] 1: Lookup: Doing byte
03/20 07:16:27 [SITE] 50: Lookup: Doing byte
03/20 07:16:27 RESOURCE: NO_CLIENT_SITE: WS01
03/20 07:16:27 [MAILSLOT] RESOURCE: Ping response 'Sam Logon Response Ex' (null) to \\WS01 Site: (null) on UDP LDAP

This conversation is summarised as:

  1. The client calls DsGetDcName, with a fully qualified domain name (the FORCE and DSP flags signify cached information to be ignored and prefers a 2000 or 2003 DC, respectively).
  2. DNS Cache finds a hit for the FQDN resource.local and the NetBIOS equivalent (RESOURCE). Note that the FORCE switch is documented as ignoring the local cache; this does not appear to be happening.
  3. The workstation uses an LDAP UDP ping to find a suitable Domain Controller. The first Domain Controller to respond wins (the preferable list has already been filtered by DNS, eg closest site)
  4. The old entry is removed from the cache and the new updated.

An equivalent network trace in a virtual testlab shows the following packets - note that a site-specific DC cannot be found in this instance, cross-forest site links are not configured. From a workstation in the user domain, querying for a Domain Controller in the trusting resource domain

nltest /dsgetdc:resource.local /FORCE

No. Source Destination Protocol Info
1 UserDC01 ResourceDC01 DNS Standard query SRV _ldap._tcp.UC1._sites.dc._msdcs
2 ResourceDC01 UserDC01 DNS Standard query response, No such name
3 UserDC01 ResourceDC01 DNS Standard query SRV _ldap._tcp.dc._msdcs
4 ResourceDC01 UserDC01 DNS Standard query response SRV 0 100 389 ResourceDC01
5 WS01 ResourceDC01 CLDAP MsgId=67 Search Request, Base DN=(null)
6 ResourceDC01 WS01 CLDAP MsgId=67 Search Entry, 1 result

The LDAP ping and reply in frames 5 and 6 query for the capabilities of the server to authenticate for the specified domain. The response is in the form of a SamLogon response, containing Domain, DC and site names, used to compare against the information used in the original DsGetDcName call to verify the DC is correct.

Note that this process simply locates a Domain Controller; with subsequent unshown packets authenticating the request.

Cross-forest Resource Access

For cross-domain/forest resource access, a combination of NTLM referral and the 200x/NT4 locator seems to be used in different scenarios. The following is a summary of a network capture when accessing resources on a member server in the resource.local domain with NTLM authentication:

  1. Initiate request from client computer directly to member server, negotiating a protocol
  2. Member server contacts resource.local Domain Controller to begin a transitive network logon by the user, from the workstation, proxied through the resource server.
  3. The DC uses cached information or the NT4 locator process (the domain is specified as a NetBIOS name) and authenticates the request, sending the request to a Domain Controller in the user domain in a SamLogon mailslot request
  4. Authentication is now verified, and resource access is then authorised

Cross-forest interactive logon

An Interactive logon using a user domain account to the resource domain uses a combination of NTLM and Kerberos, with a resultant set of Kerberos tickets on the resource.local member server for the user domain. This capture occurred when there was cross-forest site synchronization in place to optimise the netlogon DC locator process.

An excerpt from the network capture when logging on interactively from a user domain computer and user domain account to a resource.local member server is shown below:

517 "member.resource.local" "resourcedc01.resource.local" "DNS" "Standard query SRV"
518 "resourcedc01.resource.local" "member.resource.local" "DNS" "Standard query response SRV 0 100 88 "
519 "member.resource.local" "resourcedc01.resource.local" "DNS" "Standard query A"
520 "resourcedc01.resource.local" "member.resource.local" "DNS" "Standard query response A"
521 "member.resource.local" "" "CLDAP" "MsgId=1095 Search Request
522 "" "member.resource.local" "CLDAP" "MsgId=1095 Search Entry
525 "member.resource.local" "" "KRB5" "AS-REQ"

This conversation from the point of view of the member server being logged on to is summarised as:

  1. Query the DNS namespace for Domain Controllers serving the local RL1 site (which uses site coverage based on site link cost to determine the servers are the closest)
  2. The list of Domain Controllers in the closest site is returned from DNS
  3. An LDAP UDP ping is sent to the Domain Controllers, in a random order (in this case, there's only one)
  4. userdc01 responds in a timely fashion and is chosen as the DC to use.
  5. A Kerberos authentication request is sent from the resource member server directly to the Domain Controller with UDP 88.

Further traffic shows both Kerberos and NTLM authentication requests/responses, as well as ICMP and SMB directly from the resource member server and Domain Controllers.

Site synchronization

In terms of locating the closest Domain Controller for authentication and Group Policy processing, the following summarises the requirements identified above:

  1. To allow efficient interactive logons across forests, the user domain should have knowledge of the resource domain's site. Eg. Having RL1 in with appropriate site links will ensure a close DC/GC is used for authentication, universal group membership and group policy processing.
  2. To allow efficient network access to resources on member servers across forests using NTLM trusts, the resource domain controllers should have knowledge of the user domain's DC NetBIOS 1B/1C records. Across forest trusts the user site information should be in the resource domain, allowing the 2003 locator to use SRV records.
  3. To allow efficient cross-forest management of resources and querying information, each domain should be aware of cross-forest sites. This is relevant when using scripting or utilities such as nltest, which makes queries directly to objects in the foreign directory. Eg, from the domain, running scripts or utilities against the resource domain may result in unpredictable results if sites/site links are not created.

Note that this is from an Active Directory perspective; often there is network filtering in place providing 'nearest site' functionality by only allowing UDP And ICMP pings to certain hosts between forests.

To further demonstrate cross-forest DC locator with scripting, the following script can be used:

'-- FindClosestDCs.vbs
Const ADS_FORCE = 1
Set objLocator = CreateObject("ADsLocator") ' Requires ADsLocator.dll

strDomainSet = "resource.local,"

on error resume next
For Each strDomain In Split(strDomainSet, ",")
Set objADsDCINFO = objLocator.DsGetDcName(CStr(strDomain), , , ADS_CLOSEST_FLAG AND ADS_FORCE) ' Find the closest Domain Controller
If Err.Number <> 0 Then
wscript.echo "Error: Could not find DC for '" & strDomain & "' - " & err.description & " (" & err.number & ")"
strDC = objADsDCINFO.DomainControllerName ' Extract the name
wscript.echo strDomain & " DC: " & strDC
End If



NT4 Locator or 200x locator:

Multiple forest considerations:

Kerberos between forests:

How domains and forest trusts work:

Nltest syntax:

Debug in nltest:

Finding a DC and DC Locators (2000):

Domain Controller Locators (2003):

Authentication and Authorization:

Domain and forest trusts, Kerberos referral processing vs NTLM:

Cross-forest access with WebDAV:

NTLM authentication for cross-forest trusts in Windows 2000:

Communication between 2000 and 2003 forests using external trusts:

Authentication service registry settings:

Detailed example of interactive logon, remote logon (resource access) on Windows 2000 domain:

Network ports required for authentication:

How Domain Controllers Are Located in Windows XP

Wayne's World of IT (WWoIT), Copyright 2008 Wayne Martin.

Read more!

Determining AD attribute replication

This post describes a command-line method of checking the schema to see whether an attribute is replicated in Active Directory, useful for determining whether any DC is authoritative for a particular attribute, or if you'll get different results for each DC.

This queries the attribute definition in the schema of the root domain in a directory to check the System-Flags attribute and see whether the first bit is set. The first bit of the systemFlags attribute is set to 1 if the attribute is NOT replicated.

For example:

  • dsquery * cn=pwd-last-set,cn=schema,cn=configuration,DC=forestRootDomain
  • dsquery * cn=pwd-last-set,cn=schema,cn=configuration,DC=forestRootDomain -filter "!(&(systemFlags:1.2.840.113556.1.4.803:=1))"
  • dsquery * cn=last-logon,cn=schema,cn=configuration,DC=forestRootDomain -filter "!(&(systemFlags:1.2.840.113556.1.4.803:=1))"
  • dsquery * cn=last-logon-timestamp,cn=schema,cn=configuration,DC=forestRootDomain -filter "!(&(systemFlags:1.2.840.113556.1.4.803:=1))"


  1. The first query returns the attributes of the schema entry for the password last set attribute. Note that systemFlags is 16.
  2. The second query performs a bitwise AND operation against the systemFlags attribute value and 1 and the result of a NOT operation. This returns a valid match because the pwd-last-set systemFlags value is 16 (10 in hex), so the first bit is not set, meaning the attribute is replicated.
  3. The third query returns nothing because the last-logon attribute is not replicated, the systemFlags value is 17, so the first bit is set and we're negating that result.
  4. The fourth query against a 2003 DC for the new replicated last logon timestamp is a new property to 2003 that allows easy tracking of when a user logged on, regardless of their authenticating DC.

To show all replicated attributes in the AD Schema (remove the '!' to show all attributes that aren't replicated):

dsquery * cn=schema,cn=configuration,DC=forestRootDomain -filter "(&(objectClass=attributeSchema)(objectCategory=attributeSchema)(!systemFlags:1.2.840.113556.1.4.803:=1))" -limit 0


System-Flags attribute:

User Security Attributes:

Pwd-last-set attribute:

How to query Active Directory by using a bitwise filter:

Wayne's World of IT (WWoIT), Copyright 2008 Wayne Martin.

Read more!

AD Security vs Distribution Groups

This post contains information on security groups versus distribution groups in a Windows Active Directory, how to make use of mail enabled security groups and how to convert groups between different scopes.

Groups of type distribution do not have a SID, and without a security identifier, they cannot be part of an Access Control Entry or a security token, even though the members of the distribution group may be accounts that do have SIDs.

Security groups can be mail-enabled, allowing the group to be used for both access control and mail distribution, and depending on your level of service autonomy and delegation of administration this may be suitable. If you nest security and/or distribution groups, there may also be some confusion if using mail enabled security groups.

Implementing Mail-enabled Security Groups

In a simple Exchange 2003 environment, you may be able to:

  1. Convert the groups of type distribution to security, with a scope of global. Universal could be used instead of global, but this depends on whether you have a requirement for cross-domain intra-forest GC access to group membership.
  2. Ensure the security groups are mail enabled
  3. Set the 'Managed By' information on the group to an individual or local group to manage the DL and update the membership list. This will set ACLs on the AD group object to allow members to be updated in the group.
  4. Add the global group to existing local groups used to manage permissions on file shares.
  • One reason to use distribution groups rather than mail-enabled security groups is because of service and data autonomy - to separate Exchange DL admins from security group admins, using the method above would make this difficult.
  • You can use the 'dsmod group' command to change the scope and type of a 200x Active Directory group. See the examples below.

Converting an Active Directory security group from Global to Local or vice versa:

This process was tested on an XP workstation against a Windows 2000 Active Directory domain in native mode.

Identify the DN of the group by running
- dsquery group -name %GroupName%

Find the current group scope of the group just identified, by running
- dsget group %GroupDN% -scope -secgrp

Change the group scope to universal, a stepping stone required as groups can't be converted directly between global and local, by running:
- dsmod group %GroupDN% -scope u

Change the group scope to global or local (depending on the requirements), by running:
- dsmod group %GroupDN% -scope g
- dsmod group %GroupDN% -scope l

This modifies an existing group, without changing the SID, useful when the group is already used to apply permissions.


Group Objects

Group scope

Troubleshooting mail transport and distribution groups in Exchange 2000 Server and in Exchange Server 2003

Group Types

Global Catalog Server Requirement for User and Computer Logon

Wayne's World of IT (WWoIT), Copyright 2008 Wayne Martin.

Read more!

Troubleshooting cross-forest trust secure channels

When cross-forest trusts fail, the secure channel should be verified to determine that a foreign DC can be identified and contacted. This post provides information on troubleshooting techniques in this scenario, and is really only the first step in troubleshooting - establishing that there are no DC locator issues determining what should be a valid DC across the trust.

The following commands are useful for troubleshooting secure channel issues, specifically name resolution, DC locator and connectivity:

  • nltest /domain_trusts /v
  • nltest /sc_query:%trusted_domain%
  • nltest /sc_reset:%trusted_domain%[\%DCname%]
  • nslookup -debug -type=srv _ldap._tcp.dc._msdcs.%domainFQDN%

For example, in a domain called domain.local, which trusts and is trusting trustindomain.local, from the domain.local domain:

List the domain trusts:

nltest /domain_trusts /v

List of domain trusts:
0: TRUSTED (NT 5) (Direct Outbound) ( Attr: quarantined )
Dom Sid:
1: TRUSTING trustingdomain.local (NT
5) (Direct Inbound)
Dom Sid: S-1-5-21-6079874623-1494965722-2283543576
2: DOMAIN domain.local (NT 5) (Forest Tree Root) (Primary Domain) (Native)
Dom Guid: 23855607-7223-45a6-a732-13d47536f728
Dom Sid:
The command completed successfully

Query the secure channel for the trusting domain:

nltest /
Trusted DC Name \\
Trusted DC Connection Status Status = 0 0x0 NERR_Success
The command completed successfully

Reset the secure channel for the trusting domain, using standard netlogon DC locator to find a DC:

nltest /

Trusted DC Name \\
Trusted DC Connection Status Status = 0 0x0 NERR_Success
The command completed successfully

Reset the secure channel for the trusting domain to a specific Domain Controller:

nltest /\dc01

Trusted DC Name \\
Trusted DC Connection Status Status = 0 0x0 NERR_Success
The command completed successfully

When this doesn't work due to an inability to find a logon server for the specified domain:

Status = 1311 0x51f ERROR_NO_LOGON_SERVERS
I_NetLogonControl failed

Lookup the service records for a trusting domain (the output below is without 'nslookup -debug' for clarity) from the trusting domain (

nslookup -type=srv

Address: SRV service location:
priority = 0
weight = 100
port = 389
svr hostname = SRV service location:
priority = 0
weight = 100
port = 389
svr hostname = internet address = internet address =

  1. The secure channel queries have a potentially different result from each Domain Controller, which will either randomly select a cross-forest DC, or use DNS service records and site information is cross-forest site synchronisation is available.
  2. If no logon server is found, new cross-forest authentication or authorisation attempts will not work between the source Domain Controller and the target domain. If this occurs, further DNS troubleshooting should occur as this is the primary method used between 2000 and 2003 domain trusts.


How Domains and Forests Work

How DNS Support for Active Directory Works

Wayne's World of IT (WWoIT), Copyright 2008 Wayne Martin.

Read more!

Saturday, June 7, 2008

RIS cross-domain access

I had a requirement to initiate OS installs using RIS on Windows 2000 server when authenticating with cross-domain user accounts. In Windows Server 2003 this is a supported configuration, but I was pleasantly surprised that it worked from a 2003 domain to a trusting 2000 domain with a 2000 RIS server.

This test was successfully completed in a simple test environment authenticating with a cross-domain user account in a different forest than the RIS server computer account:

  • XP workstation, member of a 2003 forest/domain
  • 2000 server, which was a DC and RIS server of a 2000 domain
  • The 2000 forest trusts the 2003 forest, with a one-way external NTLM trust
  • NTFS permissions set such that the cross-domain user account has access to the RIS filesystem.

The following protocols were in use between the RIS server and the cross-domain DC:

  • TCP RPC EndPoint Mapper 135
  • TCP/UDP RPC Ephemeral ports above 1023
  • TCP NetBIOS Session Setup 139
  • TCP SMB 445
  • TCP Microsoft Directory Services 445
  • UDP Kerberos 88
  • ICMP

Wayne's World of IT (WWoIT), Copyright 2008 Wayne Martin.

Read more!

Friday, June 6, 2008

Large SMS Web Reports return Error 500

When executing large SMS report queries with thousands of rows from an SMS reporting server running on Windows Server 2003 with IIS 6.0, a HTTP 500 error may be returned. This post describes the ASP buffer overflow error that can occur and how to resolve the issue.

After turning off friendly errors, what is actually being returned is an ASP 0251 buffer overflow error, because the response buffer is not large enough. This is due to the default ASP response buffer set using the AspBufferingLimit property in the metabase, configurable at several locations throughout the metabase (see the 'AspBufferingLimit Metabase Property in IIS 6.0' reference below for more information).

Use the standard Windows adsutil.vbs VBScript to read/modify the w3svc/aspbufferinglimit. The default size returned in 2003 Server RTM is 4194304 bytes (4MB), resulting in buffer overflow errors when large queries are executed with thousands of records.

When I've seen this problem, changing the value to 8388608 (8MB) resolved the issues with queries that were found returning this error.


SMS Related article on the problem:

AspBufferingLimit Metabase Property in IIS 6.0:

ASP IIS Errors:

Tuning ASP Metabase Settings:

Wayne's World of IT (WWoIT), Copyright 2008 Wayne Martin.

Read more!

Troubleshooting SMS 2003 MP and SLP

This article provides methods to verify the operation of Management Points and Server Locator Points in Microsoft SMS 2003

Test a management point is functioning correctly:

Test a Server Locator Point is functioning correctly:


Troubleshooting Management Points for Microsoft Systems Management Server 2003

Server locator points do not work correctly in Systems Management Server 2003;en-us;886067

Wayne's World of IT (WWoIT), Copyright 2008 Wayne Martin.

Read more!

Thursday, June 5, 2008

Remotely determine physical memory

This article demonstrates different methods of finding out the memory configurations on a remote machine, useful for quick diagnosis of available physical/virtual memory. You could also use commands 1-4 in a batch file of PowerShell script to iterate through more than one remote computer.

From a Windows XP Professional Workstation, remotely targeting other workstations or servers you could use systeminfo, powershell (WMI), WMIC or perfmon:

  1. SystemInfo /s %Computer%
  2. $computer = "computer"; get-wmiobject -computerName $computer -class win32_logicalmemoryconfiguration -property TotalPhysicalMemory
  3. wmic /node:%Computer% path Win32_OperatingSystem GET FreePhysicalMemory
  4. wmic /node:"%computer%" path Win32_PerfFormattedData_PerfOS_Memory GET AvailableMBytes, PagesPerSec
  5. Use PerfMon, with the Memory 'Available MBytes' counter
  6. Add a doskey macro:
  • FM=if _$1 NEQ _ (wmic /node:$1 path Win32_OperatingSystem GET FreePhysicalMemory) else (Echo Available physical memory for %ComputerName% & wmic /node:"%computer%" path Win32_PerfFormattedData_PerfOS_Memory GET AvailableMBytes, PagesPerSec)

Note: Systeminfo provides total and available physical and virtual memory, the first WMI query provides only available physical memory, and the second wmi query checks performance data, looking for available memory and pages/sec, a good determination of a potential memory bottleneck with hard page faults.

As a side-note, if you're familiar with DOS, you may remember 'mem /c /p', and although you can no longer specify both, in XP, 2003 and Vista you can still run 'mem /c' or 'mem /p', which very conveniently (???) shows the available conventional and upper memory in the 640k boundary (looks like himem isn't loaded :)

Wayne's World of IT (WWoIT), Copyright 2008 Wayne Martin.

Read more!

Wednesday, June 4, 2008

VMware SDK with Powershell

This post provides a script using the VMware SDK with PowerShell to access a VI3 VirtualCenter server and enumerate the VMs. It's really just a proof of concept of what's possible, and a simple example of how to use the rather convoluted (in my opinion) VMware SDK.

Note that VMware have a PowerShell snap-in that makes everything VMware-related *much* easier, but there will always be lower-level tasks that the SDK is useful for.

# VMware SDK automation.
# Changes:
#  30/01/2008, Wayne Martin, Initial version
# References: 
# Note that you need the compiled VMware SDK VimService.dll for this to work, and I'm using http rather than SSL - you may have to modify your vpxd.cfg 

$vmPath = ".\VimService.dll"

$serviceURL = "http://VCServer/sdk"
$usernameSDK = "domain\user"
$passwordSDK = "password"
$localeSDK = "en_au"

$pathDatacenter = "/DATACENTER"
$pathVMFolder = $pathDatacenter + "/vm"

$svcRef = new-object ManagedObjectReference
$svcRef.type = "ServiceInstance"
$svcRef.Value = "ServiceInstance"

$service = new-object VimService
$service.Url = $serviceURL
$service.CookieContainer = new-object System.Net.CookieContainer

$svcContent = $service.RetrieveServiceContent($svcRef)
$propCollectorRef = $svcContent.PropertyCollector

$service.Login($svcContent.sessionManager, $usernameSDK, $passwordSDK, $localeSDK)
$rootFolder = $svcContent.rootFolder

$dc2f = new-object TraversalSpec
$dc2f.type = "Datacenter"
$dc2f.path = "vmFolder"
$dc2f.skip = $false
$dc2f.selectSet = new-object SelectionSpec
$dc2f.selectSet[0].name = "traverseChild"
$traversalSpec = new-object TraversalSpec
$traversalSpec.type = "Folder"
$ = "traverseChild"
$traversalSpec.path = "childEntity"
$traversalSpec.skip = $false
$traversalSpec.selectSet = new-object SelectionSpec
$sspecElement = new-object SelectionSpec
$traversalSpec.selectSet = $sspecElement, $dc2f
$traversalSpec.selectSet[0].name = $

$pfSpec = new-object PropertyFilterSpec
$pfSpec.propSet = new-object PropertySpec
$pfSpec.propSet[0].type = "VirtualMachine"
$pfSpec.propSet[0].all = $false
$pfSpec.propSet[0].pathSet = "", "config.uuid", "config.extraConfig", "config.hardware", ""
#$pfSpec.propSet[0].pathSet = "", ""

$pfSpec.objectSet = new-object ObjectSpec
$pfSpec.objectSet[0].obj = $rootFolder
$pfSpec.objectSet[0].skip = $false
$pfSpec.objectSet[0].selectSet = new-object SelectionSpec
$pfSpec.objectSet[0].selectSet[0] = $traversalSpec

$pfSpecRes = new-object PropertyFilterSpec 
$pfSpecRes = $pfSpec

$retProp = $service.retrieveProperties($propCollectorRef, $pfSpecRes)

for ($i= 0; $i - $retProp.Length; $i += 1) {
 $ObjContent = $retProp[$i]
 $propSet = $ObjContent.propSet
 for ($y= 0; $y - $propSet.Length; $y += 1) {
  $dynProp = $propSet[$y]
  if (!$dynProp.val.getType().IsArray) {
   write-host $dynProp.Name ": " $dynProp.val
  } else {
   for ($z= 0; $z - $dynProp.Val.Length; $z += 1) {
    $OCdynProp = $dynProp.val[$z]
    If ($OCdynProp.getType().ToString() -eq "GuestNicInfo") {
     write-host "MAC:" $OCdynProp.get_macAddress()
     write-host "IP:" $OCdynProp.get_IPAddress()
     write-host "Network:" $OCdynProp.get_network()
    } else {
     write-host $OCdynProp.get_key() ": " $OCdynProp.get_value()

#for ($i= 0; $i - $retProp.Length; $i += 1) {
# $ObjContent = $retProp[$i]
# $propSet = $ObjContent.propSet
# write-host $propSet[0].val "-" $propSet[1].val[0].macAddress "-" $propSet[1].val[0].IPAddress
#get-member -i $vmFolderMOR | write-host

Wayne's World of IT (WWoIT), Copyright 2008 Wayne Martin.

Read more!

Monday, June 2, 2008

Spinning Excel Pie Chart

Have you ever wanted to create a pie chart in Excel that spins? I didn't think so, but anyway, below is a little bit of VBA that will do the job if you've got a toggle button on your spreadsheet (ToggleButton1).

Option Explicit

Dim Spinning As Boolean
Dim RotateCalled As Integer

Public Sub Rotate()
    RotateCalled = RotateCalled + 1
    If RotateCalled > 1 Then Exit Sub   ' Only call once
    Dim x As Integer
        If Spinning = True Then
            Worksheets("Sheet1").ChartObjects(1).Chart.PieGroups(1).FirstSliceAngle = x
            x = x + 1
            If x = 360 Then x = 0
        End If
        Call Wait(0.05)
End Sub

Sub Wait(tSecs As Single)
    Dim sngSec As Single
    sngSec = Timer + tSecs
    Do While Timer < sngSec
End Sub

Private Sub ToggleButton1_Click()
    If ToggleButton1.Value = True Then
      Spinning = True
      ToggleButton1.Caption = "Stop"
      Spinning = False
      ToggleButton1.Caption = "Go"
    End If
    Call Rotate
End Sub
 1 Then Exit Sub   ' Only call once
    Dim x As Integer
        If Spinning = True Then
            Worksheets("Sheet1").ChartObjects(1).Chart.PieGroups(1).FirstSliceAngle = x
            x = x + 1
            If x = 360 Then x = 0
        End If
        Call Wait(0.05)
End Sub

Sub Wait(tSecs As Single)
    Dim sngSec As Single
    sngSec = Timer + tSecs
    Do While Timer < sngSec
End Sub

Private Sub ToggleButton1_Click()
    If ToggleButton1.Value = True Then
      Spinning = True
      ToggleButton1.Caption = "Stop"
      Spinning = False
      ToggleButton1.Caption = "Go"
    End If
    Call Rotate
End Sub

Wayne's World of IT (WWoIT), Copyright 2008 Wayne Martin.

Read more!

All Posts

printQueue AD objects for 2003 ClusterVirtualCenter Physical to VirtualVirtual 2003 MSCS Cluster in ESX VI3
Finding duplicate DNS recordsCommand-line automation – Echo and macrosCommand-line automation – set
Command-line automation - errorlevels and ifCommand-line automation - find and findstrBuilding blocks of command-line automation - FOR
Useful PowerShell command-line operationsMSCS 2003 Cluster Virtual Server ComponentsServer-side process for simple file access
OpsMgr 2007 performance script - VMware datastores...Enumerating URLs in Internet ExplorerNTLM Trusts between 2003 and NT4
2003 Servers with Hibernation enabledReading Shortcuts with PowerShell and VBSModifying DLL Resources
Automatically mapping printersSimple string encryption with PowerShellUseful NTFS and security command-line operations
Useful Windows Printer command-line operationsUseful Windows MSCS Cluster command-line operation...Useful VMware ESX and VC command-line operations
Useful general command-line operationsUseful DNS, DHCP and WINS command-line operationsUseful Active Directory command-line operations
Useful command-linesCreating secedit templates with PowerShellFixing Permissions with NTFS intra-volume moves
Converting filetime with vbs and PowerShellDifference between bat and cmdReplica Domain for Authentication
Troubleshooting Windows PrintingRenaming a user account in ADOpsMgr 2007 Reports - Sorting, Filtering, Charting...
WMIC XSL CSV output formattingEnumerating File Server ResourcesWMIC Custom Alias and Format
AD site discoveryPassing Parameters between OpsMgr and SSRSAnalyzing Windows Kernel Dumps
Process list with command-line argumentsOpsMgr 2007 Customized Reporting - SQL QueriesPreventing accidental NTFS data moves
FSRM and NTFS Quotas in 2003 R2PowerShell Deleting NTFS Alternate Data StreamsNTFS links - reparse, symbolic, hard, junction
IE Warnings when files are executedPowerShell Low-level keyboard hookCross-forest authentication and GP processing
Deleting Invalid SMS 2003 Distribution PointsCross-forest authentication and site synchronizati...Determining AD attribute replication
AD Security vs Distribution GroupsTroubleshooting cross-forest trust secure channels...RIS cross-domain access
Large SMS Web Reports return Error 500Troubleshooting SMS 2003 MP and SLPRemotely determine physical memory
VMware SDK with PowershellSpinning Excel Pie ChartPoke-Info PowerShell script
Reading web content with PowerShellAutomated Cluster File Security and PurgingManaging printers at the command-line
File System Filters and minifiltersOpsMgr 2007 SSRS Reports using SQL 2005 XMLAccess Based Enumeration in 2003 and MSCS
Find VM snapshots in ESX/VCComparing MSCS/VMware/DFS File & PrintModifying Exchange mailbox permissions
Nested 'for /f' catch-allPowerShell FindFirstFileW bypassing MAX_PATHRunning PowerSell Scripts from ASP.Net
Binary <-> Hex String files with PowershellOpsMgr 2007 Current Performance InstancesImpersonating a user without passwords
Running a process in the secure winlogon desktopShadow an XP Terminal Services sessionFind where a user is logged on from
Active Directory _msdcs DNS zonesUnlocking XP/2003 without passwords2003 Cluster-enabled scheduled tasks
Purging aged files from the filesystemFinding customised ADM templates in ADDomain local security groups for cross-forest secu...
Account Management eventlog auditingVMware cluster/Virtual Center StatisticsRunning scheduled tasks as a non-administrator
Audit Windows 2003 print server usageActive Directory DiagnosticsViewing NTFS information with nfi and diskedit
Performance Tuning for 2003 File ServersChecking ESX/VC VMs for snapshotsShowing non-persistent devices in device manager
Implementing an MSCS 2003 server clusterFinding users on a subnetWMI filter for subnet filtered Group Policy
Testing DNS records for scavengingRefreshing Computer Account AD Group MembershipTesting Network Ports from Windows
Using Recovery Console with RISPAE Boot.ini Switch for DEP or 4GB+ memoryUsing 32-bit COM objects on x64 platforms
Active Directory Organizational Unit (OU) DesignTroubleshooting computer accounts in an Active Dir...260+ character MAX_PATH limitations in filenames
Create or modify a security template for NTFS perm...Find where a user is connecting from through WMISDDL syntax in secedit security templates

About Me

I’ve worked in IT for over 20 years, and I know just about enough to realise that I don’t know very much.