Early in my life as a network administrator, I fought many battles just trying to keep my network together. Because the original architects of the network never really planned for pesky tasks such as checking security, I had to implement cheap but effective methods to query settings on remote machines. So I did what all good administrators do: I wrote a script.
I wrote the script in Perl. For those who are unfamiliar with Perl, it's a powerful, extensible open-source scripting language. It has been ported to most platforms (e.g., UNIX, Macintosh, Win32, VMS), so you can use it to write scripts that can run on all your systems.The script interacts with the Win32 registry, which stores most of Win32's configuration information, to check some settings for security purposes. The script implements a simple form of autodiscovery to ensure that it inspects all machines on the network.
Scripting in Perl
Perl has been around for more than 10 years and has become the language of choice for most administrator who want to use one language across multiple platforms. You can download Perl precompiled binaries for the Win32 platform from ActiveState (http://www.activestate.com). You can find information about Perl, including details about obtaining and compiling the source code, on a number of Web sites. The Perl site that O'Reilly & Associates sponsors (http://www.perl.com) has all sorts of articles, columns, and other information that's useful for any Perl user.
One of Perl's most compelling advantages is its widespread support. You can go to one of many repository sites and download ready-made scripts, modules, and extensions. Modules are libraries of functions; extensions are DLLs that provide functionality above and beyond the language itself. Two such modules and extensions that are available for Win32 platforms are the Net::Ping module, which provides Internet Control Message Protocol (ICMP) and TCP ping functionality, and the Win32::Registry extension, which provides access to the Win32 registry. Both Net::Ping and Win32::Registry come with the ActivePerl distribution from ActiveState.
By combining this Perl module and extension, you can easily write a script that autodiscovers Win32 machines on the network and interacts with their registries. The code in Listing 1, page 42, shows how to use Net::Ping and Win32::Registry in tandem to elicit information about Windows machines across your network and to help secure them.
You can use one of several ways to determine what machines exist on your network. One way is to use Microsoft Active Directory Service Interfaces (ADSI) to query Active Directory (AD). In Perl, you can use COM to interact with ADSI, so you would load the Win32::OLE extension. (Alternatively, you can use the Lightweight Directory Access Protocol—LDAP—but I prefer to use ADSI for various authentication and authorization reasons.) However, you can't use ADSI for networks that don't use AD, such as Windows NT domains. Another way to discover a domain's machines is to use the Win32::AdminMisc extension's GetMachines() function or the Win32::Lanman extension's NetQueryDisplayInformation() function to query a domain controller (DC).
The problem with querying AD or a DC is that they give you a list of machines in a given domain. These machines might be offline, or they might not still be in use (think of machines that have long been removed from the domain but whose domain accounts still exist). Additionally, the list would include only machines in the specified domain, not machines in other domains or workgroups on the network.
To circumvent these problems, the script implements a form of autodiscovery. At callout C in Listing 1, the script creates a Net::Ping object that pings each IP address on a given subnet of your network. By default, Net::Ping uses the ICMP protocol for pinging; however, the object also supports TCP pinging. (Check the Net::Ping module's documentation for further details.) The code at callout E loops through the IP range specified in the code at callout A. The script pings each IP address and calls the ProcessHost() subroutine for each machine that responds.
Processing the Registry
The ProcessHost() subroutine uses the Win32::Registry extension to query a remote machine's registry. First, the subroutine attempts to connect to the remote machine. This connection fails if the remote machine isn't a Win32 platform. Likewise, the connection fails if a remote Win32 machine prevents access to its registry (e.g., if the person running the script doesn't have permissions to access the machine's registry) or if the remote registry service is disabled.
When the subroutine makes a connection, it attempts to access four registry values. When the subroutine successfully opens a registry subkey, it queries for a particular value, outputs the value to the screen, closes the subkey, and repeats the logic for the remaining values. The four values are the machine's network name, the last user to log on, the approximate speed of the machine in megahertz, and a list of null session shares.
Null session shares are shared directories that have no associated permissions; therefore, they don't require client authentication. Anyone can gain entry to these directories without being authenticated. Null session shares are a security exposure, so checking your systems for them and eliminating the ones that aren't necessary is a good idea. For more information about when null session shares are required, see the sidebar "Using Null Session Shares," page 46.
The code at callout F retrieves and processes the list of null session shares. The line at callout G splits the null-delimited list into the individual share names. Then, for each share name, the code at callout H calls the IsShareAllowed() subroutine to check whether the share name is in the @VALID_SHARES array (defined in the code at callout B, page 42). For my purposes, the array contains the names of four shares that my company allows as null session shares. These four shares let Microsoft Dfs and Computer Associates' (CA's) Inoculan AntiVirus services work. You'll need to modify this array to contain the null session share names that you allow on your network.
The code at callout H includes a line, which I've commented out, that removes from the null session shares list in the registry any share names that aren't in the @VALID_SHARES array and stores the updated list in the registry. If you want to use FixNullShares.pl to remove unapproved null session share names from the registry on your machines, simply remove the comment (#) character from the beginning of the commented-out line. But before you do so, make sure that the @VALID_SHARES array contains the correct share names for your network or for a particular machine.
Logging the Results
Tracking the results of a script is often useful. The code at callout D, page 42, gives the log file the same path as that of the FixNullShares.pl script; however, the log has a .log extension instead of a .pl extension. After opening the log file, the script calls the select() function to select the LOG file handle. The script then disables output buffering for the LOG file handle (at this point, the "selected" file handle) so that the log data is written to disk immediately. This technique makes troubleshooting easier because you don't have to wait for buffers to flush before you can review the log file. The script then selects the original file handle so that output makes its way to the screen instead of to the log file.
Later, in the code at callout I, page 44, the printf() function sends information to the log file. However, notice that the script checks the result of the if(fileno LOG) command and calls the printf() function only when the LOG file handle exists. If the script was unable to create the log file, the result is a zero value and the script won't execute the printf() and close() functions. Without the if(fileno LOG) check, a runtime error would occur if the file failed to open and the script would be terminated.
Customizing the Script
The FixNullShares.pl script is a great way to discover a machine's IP address, which is useful even if your network uses DHCP to assign IP addresses from a pool. The script assumes that you're using a class C network, in which each subnet contains host addresses between 0 and 255, but you can easily adapt the script to any type of network.
The script accepts up to three optional parameters. The first parameter indicates the networking subnet; the second and third parameters represent the minimum and maximum host addresses, respectively. For example, the command
perl FixNullShares.pl 192.168.33 65 189
scans all machines between and including the addresses 192.168.33.65 and 192.168.33.189.
As I mentioned earlier, you must change FixNullShares.pl's list of acceptable null session shares to the list of share names that you allow on your network. Simply modify the @VALID_SHARES array to list the shares you allow.
The biggest changes that you'll want to consider are possible changes in the tasks that FixNullShares.pl performs. For example, instead of the script finding the most recent user of your servers, it could query or update Distributed COM (DCOM) settings, DHCP settings, service configurations, or software settings. Note that the user who runs the script must have proper permissions to access the registry on the remote servers. Typically, a member of the Domain Admins group would have the appropriate permissions.
The Thrill of Remote Registry
Using a script to discover and change remote servers' registry settings sure beats walking to each machine in your network, logging on, and making the changes manually. Because the script is written in Perl, you can run it for free. Just download Perl from ActiveState and install it on the machine that will run the script.
Marry Perl with the Win32 registry, and you have a great tool that you can use for multiple purposes. Hundreds of Perl modules that you can download and use for free are available on the Internet. The FixNullShares.pl script demonstrates the flexibility this free code gives you.