Softpanorama
(slightly skeptical) Open Source Software Educational Society

May the source be with you, but remember the KISS principle ;-)

Softpanorama Search

Perl-based Integrity Checkers

News Recommended Links Afick (perl) Fcheck (Perl) Viper (Perl)
Nabou Slipwire (Perl) Toby Humor Etc

Integrity checkers that support MD5 seems to be preferable as MD5 checksums are often available from vendors. Perl implementations are generally more flexible that written in C. afick is current favorite and it got some positive press:

With new threats showing up every day, administrators find it increasingly hard to establish continued trust with their filesystems. Luckily, it's easier than you might think to maintain omniscient control of your filesystem. Through effective use of a filesystem integrity checker, you can keep a watchful eye on every aspect of an important machine's filesystem.

There are several filesystem integrity checker applications, both commercial and open source. I chose to deploy afick, because it is written in Perl, which makes it lightweight and easily portable between different operating systems. Though by nature designed for the command line, afick also has an optional Webmin module and a graphical interface written in perl-Tk.

For this article we will focus on the command-line implementation on a SUSE Enterprise 8.0 server, but what you see here should be applicable to just about any *nix distribution.

Old News ;-)

[Oct 23, 2008] Another File Integrity Checker 2.12 by Gerbier Eric

Perl-based, so modifiable by admin. See also Integrity Checkers

About: afick is another file integrity checker, designed to be fast and fully portable between Unix and Windows platforms. It works by first creating a database that represents a snapshot of the most essential parts of your computer system. You can then run the script to discover all modifications made since the snapshot was taken (i.e. files added, changed, or removed). The configuration syntax is very close to that of aide or tripwire, and a graphical interface is provided.

Changes: The code now works with perl 5.10. On Windows, afick_planning now sends a report instead of a summary and uses the "LINES" macro. On Unix, a new MOUNT macro allows you to use a remote database in afick_cron. Udev files were removed from scan. The tar.gz installer was recoded to display better diagnostics.

O'Reilly -- News -- Detecting Local Filesystem Changes with Perl

08/01/2000

by David N. Blank-Edelman, author of Perl for System Administration

It is not uncommon for system administrators to have to drop whatever they are working on to deal with the security problem du jour. Some of these problems involve serious breaches of security. In these cases, the first question asked is often, "What has the intruder done?" In my recently released O'Reilly book, Perl for System Administration, I begin the chapter on security and network monitoring with a discussion of some of the available Perl tools that can help answer this question. Here's an excerpt from that chapter, which deals with finding changes made to a local filesystem:

Filesystems are an excellent place to begin our exploration into change-checking programs. We're going to explore ways to check if important files like operating system binaries and security-related files (e.g., /etc/passwd or msgina.dll ) have changed. Changes to these files made without the knowledge of the administrator are often signs of an intruder. There are some relatively sophisticated cracker tool-kits available on the Net that do a very good job of installing Trojan versions of important files and covering up their tracks. That's the most malevolent kind of change we can detect. On the other end of the spectrum, sometimes it is just nice to know when important files have been changed (especially in environments where multiple people administer the same systems). The techniques we're about to explore will work equally well in both cases.

The easiest way to tell if a file has changed is to use the Perl functions stat() and lstat(). These functions take a filename or a filehandle and return an array with information about that file. The only difference between the two functions manifests itself on operating systems like Unix that support symbolic links. In these cases lstat() is used to return information about the target of a symbolic link instead of the link itself. On all other operating systems the information returned by lstat() should be the same as that returned by stat(). Using stat() or lstat() is easy:

@information = stat("filename");
As demonstrated in Chapter 3, "User Accounts," we can also use Tom Christiansen's File::Stat module to provide this information using an object-oriented syntax. The information returned by stat() orlstat() is operating-system dependent. stat() and lstat() began as Unix system calls, so the Perl documentation for these calls is skewed towards the return values for Unix systems. See Table 10-1 in Perl for System Administration for a comparison between the values returned by stat() under UNIX and those returned by stat() on Windows NT/2000 and MacOS.

In addition to stat() and lstat(), other non-Unix versions of Perl have special functions to return attributes of a file that are peculiar to that OS. See Chapter 2, "Filesystems," for discussions of functions like MacPerl::GetFileInfo() and Win32::FileSecurity::Get().

Once you have queried the stat()ish values for a file, the next step is to compare the "interesting" values against a known set of values for that file. If the values changed, something about the file must have changed. Here's a program that both generates a string of lstat() values and checks files against a known set of those values. We intentionally exclude last access time from the comparison because it changes every time a file is read. This program takes either a -p filename argument to print lstat() values for a given file or a -c filename argument to check the lstat() values all of the files listed in filename.

use Getopt::Std;

# we use this for prettier output later in &printchanged()
@statnames = qw(dev ino mode nlink uid gid rdev size mtime
        ctime blksize blocks);
getopt('p:c:');
die "Usage: $0 [-p <filename>|-c <filename>]\n" unless ($opt_p or $opt_c);
if ($opt_p){
    die "Unable to stat file $opt_p:$!\n" unless (-e $opt_p);
    print $opt_p,"|",join('|',(lstat($opt_p))[0..7,9..12]),"\n";
    exit;
}

if ($opt_c){
    open(CFILE,$opt_c) or die "Unable to open check file $opt_c:$!\n";
    while(<CFILE>){
        chomp;
        @savedstats = split('\|');
        die "Wrong number of fields in line beginning with $savedstats[0]\n"
            unless ($#savedstats == 12);
        @currentstats = (lstat($savedstats[0]))[0..7,9..12];
        # print the changed fields only if something has changed
        &printchanged(\@savedstats,\ @currentstats)
            if ("@savedstats[1..13]" ne "@currentstats");
    }
    close(CFILE);
}
# iterates through attributes lists and prints any changes between
# the two
sub printchanged{
    my($saved,$current)= @_;
    # print the name of the file after popping it off of the array read
    # from the check file
    print shift @{$saved},":\n";
    for (my $i=0; $i < $#{$saved};$i++){
        if ($saved->[$i] ne $current->[$i]){
            print "\t".$statnames[$i]." is now ".$current->[$i];
            print " (should be ".$saved->[$i].")\n";
        }
    }
}

To use this program, we might type checkfile -p /etc/passwd>>checksumfile. checksumfile should then contain a line that looks like this:

/etc/passwd|1792|11427|33060|1|0|0|24959|607|921016509|921016509|8192|2

We would then repeat this step for each file we want to monitor. Then, running the script with checkfile -c checksumfile will show any changes. For instance, if I remove a character from /etc/passwd, this script will complain like this:

/etc/passwd:
size is now 606 (should be 607)
mtime is now 921020731 (should be 921016509)
ctime is now 921020731 (should be 921016509)
There's one quick Perl trick in this code to mention before we move on. The following line demonstrates a quick-and-dirty way of comparing two lists for equality (or lack thereof):
if ("@savedstats[1..12]" ne "@currentstats");
The contents of the two lists are automatically "stringified" by Perl by concatenating the list elements with a space between them, as if we typed:
join(" ",@savedstats[1..12]))
and then the resulting strings are compared. For short lists where the order and number of the list elements is important, this technique works well. In most other cases, you'll need an iterative or hash solution like those documented in the Perl FAQs.

Now that you have file attributes under your belt, I've got bad news for you. Checking to see that a file's attributes have not changed is a good first step, but it doesn't go far enough. It is not difficult to alter a file while keeping attributes like the access and modification times the same. Perl even has a function, utime(), for changing the access or modification times of a file. Time to pull out the power tools.

Detecting change in data is one of the fortes of a particular set of algorithms known as "message-digest algorithms." Here's how Ron Rivest describes a particular message-digest algorithm called the "RSA Data Security, Inc. MD5 Message-Digest Algorithm" in RFC1321:

The algorithm takes as input a message of arbitrary length and produces as output a 128-bit "fingerprint" or "message digest" of the input. It is conjectured that it is computationally infeasible to produce two messages having the same message digest, or to produce any message having a given pre-specified target message digest.
For our purposes this means that if we run MD5 on a file, we'll get a unique fingerprint. If the data in this file were to change in any way, no matter how small, the fingerprint for that file will change. The easiest way to harness this magic from Perl is through the Digest module family and its Digest::MD5 module.

The Digest::MD5 module is easy to use. You create a Digest::MD5 object, add the data to it using the add() or addfile() methods, and then ask the module to create a digest (fingerprint) for you. To compute the MD5 fingerprint for a password file on Unix, we could use something like this:

use Digest::MD5 qw(md5);
$md5 = new Digest::MD5;
open(PASSWD,"/etc/passwd") or die "Unable to open passwd:$!\n";
$md5->addfile(PASSWD);
close(PASSWD);
print $md5->hexdigest."\n";
The Digest::MD5 documentation demonstrates that we can string methods together to make the above program more compact:
use Digest::MD5 qw(md5);
open(PASSWD,"/etc/passwd") or die "Unable to open passwd:$!\n";
print Digest::MD5->new->addfile(PASSWD)->hexdigest,"\n";
close(PASSWD);
Both of these code snippets print out:
a6f905e6b45a65a7e03d0809448b501c
If we make even the slightest change to that file, the output changes. Here's the output after I transpose just two characters in the password file:
335679c4c97a3815230a4331a06df3e7
Any change in the data now becomes obvious. Let's extend our previous attribute-checking program to include MD5:

use Getopt::Std;
use Digest::MD5 qw(md5);

@statnames = qw(dev ino mode nlink uid gid rdev size mtime
        ctime blksize blocks md5);
getopt('p:c:');
die "Usage: $0 [-p <filename>|-c <filename>]\n"
    unless ($opt_p or $opt_c);
if ($opt_p){
    die "Unable to stat file $opt_p:$!\n" unless (-e $opt_p);
    open(F,$opt_p) or die "Unable to open $opt_p:$!\n";
    $digest = Digest::MD5->new->addfile(F)->hexdigest;
    close(F);
    print $opt_p,"|",join('|',(lstat($opt_p))[0..7,9..12]),"|$digest","\n";
    exit;
}
if ($opt_c){
    open(CFILE,$opt_c) or die "Unable to open check file $opt_c:$!\n";
    while (<CFILE>){
        chomp;
        @savedstats = split('\|');
        die "Wrong number of fields in \'$savedstats[0]\' line.\n"
            unless ($#savedstats == 13);

        @currentstats = (lstat($savedstats[0]))[0..7,9..12];
        open(F,$savedstats[0]) or die "Unable to open $opt_c:$!\n";
        push(@currentstats,Digest::MD5->new->addfile(F)->hexdigest);
        close(F);
        &printchanged(\@savedstats,\ @currentstats)
            if ("@savedstats[1..13]" ne "@currentstats");
    }
    close(CFILE);
}

sub printchanged {
    my($saved,$current)= @_;
    print shift @{$saved},":\n";
    for (my $i=0; $i <= $#{$saved};$i++){
        if ($saved->[$i] ne $current->[$i]){
            print " ".$statnames[$i]." is now ".$current->[$i];
            print " (".$saved->[$i].")\n";
        }
    }
}

freshmeat.net Project details for System Configuration Collector for Windows

Written in Perl
System Configuration Collector for Windows collects configuration data from Windows systems and compares the data with the previous run. Differences are added to a logbook, and all data can be sent to the server part of SCC.

Release focus: Minor bugfixes

Changes:
The current snapshot of scc-log is removed after all data is collected correctly, and the syntax of a snapshot is checked on the first installation. The DIV tag with class SCC_LOG_ENTRY in scc-log2html was corrected. Nested DIV tags were added in scc-snap2html to support formatting of each section of the snapshot.

Author:
Siem Korteweg [contact developer]

 

AFICK (Another File Integrity CHecker)

Afick is a security tool, very close from the well known tripwire. It allows to monitor the changes on your files systems, and so can detect intrusions.

It's designed to be quick and portable. For now, it has been tested on but it should work on any computer with perl and its standard modules.

[May 2, 2005]  The Second Commandment of system administration (NewsForge)

NewsForge takes a look at integrity checkers. "Each integrity checker is a little different, so do some research before deciding on one. There are many excellent integrity checking applications out there, but the one I recommend and prefer is called afick (Another File Integrity ChecKer). Afick offers several advantages over integrity checkers such as Tripwire and AIDE. The first and foremost difference is that afick is written in Perl, which gives it the advantage of speed. Afick finishes the initialization of the database that stores filesystem attributes almost a minute faster than AIDE. Being written in Perl also means that afick is highly portable between operating systems."

[Feb 20, 2005] macosxhints - A Perl script to watch files for changes

OS X applications make use of various support files and these files get read and written quite frequently behind the scenes. Especially when troubleshooting some problem, it is often useful to be notified when relevant files get modified. I wrote a Perl script (which I call watchfile [4kb download from macosxhints.com]) that prints a message when one of the specified files gets modified. As usual, you need to make the script file executable (with chmod +x) and install the script in a folder that is in your shell's execution path.

You execute this script in a Terminal window, specifying the file or files you want it to watch in the usual way (a space-separated list of filenames). For example, if you want it to watch the file ~/Library -> Preferences -> com.apple.finder.plist, you would run the command:
 % watchfile ~/Library/Preferences/com.apple.finder.plist
Or if you wanted to watch all of the com.apple preference files, you would run the command:
 % watchfile ~/Library/Preferences/com.apple.*
The script will print a message "info stored" when it starts watching each file and then it will check on the file each 10 seconds after that and print a message if anything about the file has changed. The messages are somewhat cryptic, indicating which things have changed since the last check. Read the rest of the hint for a breakdown on the messages you might see...

[robg adds: I tried this script, and it's really pretty interesting. It's amazing to see how much stuff is changed, and how often it's changed.]

Here's a list of the things that might be reported:
Each time a change is reported, the script also shows the result of doing ls -l on the file.

Options:
In my testing, I found it very interesting to watch the com.apple preference files, especially with the -atime option. I was surprised how many preference files are being accessed and how often they get written to (and often recreated).

One final note: Although I haven't yet tried it, I think the script should need only small changes to get it to work on other operating systems (anywhere that Perl is installed). It should work as is on any Linux system. After changing the line that does the
ls -l, it should even work on Windows if you install Perl.

[Feb 18, 2005] NewsForge Check your filesystems' integrity with afick

With new threats showing up every day, administrators find it increasingly hard to establish continued trust with their filesystems. Luckily, it's easier than you might think to maintain omniscient control of your filesystem. Through effective use of a filesystem integrity checker, you can keep a watchful eye on every aspect of an important machine's filesystem.

There are several filesystem integrity checker applications, both commercial and open source. I chose to deploy afick, because it is written in Perl, which makes it lightweight and easily portable between different operating systems. Though by nature designed for the command line, afick also has an optional Webmin module and a graphical interface written in perl-Tk.

For this article we will focus on the command-line implementation on a SUSE Enterprise 8.0 server, but what you see here should be applicable to just about any *nix distribution.

Installation

You can either download and build afick from the source code, or, if you're using a package-based distribution, you can install from an RPM or Debian binary packages. Let's walk through building it from source. To begin, download and unpack the latest version, navigate to the afick directory, and run the following command:

# perl Makefile.pl

Since you aren't installing the GUI, you can safely ignore any errors about missing perl-Tk modules.

Next, type make install to install the console tool. When your machine finishes copying all the necessary files, afick will be installed on your system. Installation is only half the battle, though. The real fun lies in the configuration and testing phase.

Configuration

Afick begins by creating a database of values representing the current state of your filesystem. When you run it later in update mode, afick compares the current filesystem to the original and notes changes.

The exact attributes of your filesystem that are checked are controlled by a configuration file. Afick provides a default configuration file for both Linux and Windows in the directory where it was originally unpacked. In our case, we are interested in the file linux.conf. You can modify a wealth of options in this file, but we will focus only on the essentials.

Afick provides multiple ways to check every file and directory on your system, so no one configuration file is going to work for everyone. For instance, I am running PHPNuke on a Web server that includes forums, which are going to change constantly as users post items and change their preferences. I don't want those changes dumped into my mailbox every day, possibly burying something important. Someone else with a static Web site, however, might want to see that that content is never changed unexpectedly, and would therefore closely monitor that directory. It takes a bit of trial and error to fine-tune afick (or any other integrity checker for that matter) for your specific needs.

Let's begin by initializing the database with the command:

# afick -c /[path_to_linux.conf]/linux.conf -i

The -c tells afick where to find the configuration file it should use, while the -i instructs it to make an initial copy of the filesystem database. This process will take a few minutes. Once it has finished, let your server run for a while, then compare the databases with this slightly different command:

# afick -c /[path_to_linux.conf]/linux.conf -k

The -k argument tells afick to compare the current filesystem against the database specified in the first line of the linux.conf file, which is the initial database. Any changes will be noted on stdout.

Repeat this process a few times until you're getting a feel for what is changing and how so. For instance, if you've got busy log files somewhere outside of /var, they might produce a bundle of changes every time you run afick, which will create white noise around potentially useful data. After you've got a list of repeat offenders, you can tune the linux.conf file.

The linux.conf file actually has a decent description of all the file attributes you can monitor, including device, inode, permissions, owner, last time modified, and several others. You can even create your own rule sets for certain types of files and directories. For instance, you don't want afick reporting warnings about the individual files in /var/log being modified, as these files are going to be modified almost constantly on some systems. To create a rule set that would check the user and group ownership, the device they reside on, and the permissions, you would first add:

#alias
specialrule = u+g+d+p

Then, to apply this custom rule to the /var/log directory, you would add the following line to the =/Dir section of the conf file:

/var/log specialrule

If you want to define a rule set that ignores the /tmp directory, checks only the files ending in .backup in /root, and ignores all files in /home/user that end in .old, you would add the following lines to the alias section of the config file.

!/tmp
/root/*.backup special_rule
exclude_re := /home/user/.*\.old

Afick recognizes the standard Unix wild cards and regular expressions in rule sets. With a little bit of tweaking, you can tune afick to completely monitor your filesystem in all the necessary places, while ignoring the spots that would generate useless noise.

After you've spent some time tweaking your configuration file you need to ensure that afick itself cannot be modified. The most secure way to accomplish this is to put the database, found by default at /var/lib/afick/afick.pag, somewhere that is write-only. Unfortunately a diskette isn't an option because of the size of the afick database; my database is roughly 15MB for a 4GB server. I recommend using an Iomega Zip disk for a couple reasons. Primarily, you can switch a Zip disk from read-only to read-write with the flip of a switch. This is convenient because every time you make a change to your filesystem you'll need to update the database to clear the warnings that afick will produce every time it runs thereafter, which could lead to wasting a lot of CDs if you tried to deploy the database on a CD-R.

No matter where you store your database, you still need to tell the configuration file where to find it. Mount your Zip drive (or your CD) and copy /var/lib/afick/afick.pag to your mount point. Then change the entry for the database location in the first section of the config file to represent the path to your removable media.

database=/mnt/zip/afick

In addition to storing the database on read-only media, I also choose to err on the side of paranoia, and I keep my config file on read-only media as well. In this case, it's a diskette that I've write-protected. This doesn't prevent afick from being run with a separate config file created locally on the server, but it does allow me to be sure that no small detail within the file can be changed without someone physically touching my servers.

Automating it

The final step in configuring afick is automating it. (Note that this is not strictly necessary if you wish to run afick only after certain tasks.) The easiest way to automate afick is with the afick.cron script included in the original directory where you unpacked the source code. If you installed via an RPM, then afick.cron was implemented upon your installation, and should be emailing root as changes occur. If you followed the instructions here and installed from source, you have to add it manually. At your command prompt, just type:

crontab -e
0*/2 * * * root /[path_to_afick.cron]/afick.cron

Then save and exit the editor. This tells your operating system to run afick once every two hours and mail the results, if any, to root.

Updating it

Every time you make a change to a filesystem, you'll want to update the afick database. Unfortunately, this is a point where security can begin to become an inconvenience. You must physically be at the server to change the Zip disk to read-write mode so the database can be updated.

To initiate update mode, simply replace the -k in the afick command line with -u. This will update the database to include any changes that have happened since the last time afick was run. Since afick will continue using the new database to ensure file integrity, you should always run afick once in compare mode before updating it, to be sure that the database you are about to create won't report recently compromised system commands or files as legitimate. As a further level of protection, afick has built-in integrity checks it performs on its own executables to ensure that afick itself hasn't been modified.

On a short side note, you can also use the update feature of afick to monitor exactly what changes a program's installation procedure makes to a filesystem by using the update feature immediately before and immediately after the installation. This is extremely useful in situations where you cannot verify the authenticity of an application before you install it. Just make sure when you test it with afick that you don't do so on a mission-critical machine. You can also use afick for retroactive testing to ensure uninstalling the software actually returns your filesystem to its previous state.

The future of afick

Afick is a work in progress. In recent conversations, developer Eric Gerbier said he intends to include in future releases a daemon-enabled version that doesn't rely on cron to run afick, thus delivering real-time filesystem monitoring. An option to export afick's results in HTML/XML is in the works for version 3.0, due out sometime in the next few months.

No system will ever be completely safe from malicious users and unauthorized access. If a machine under your control becomes compromised, you must have the proper precautions in place to quickly mitigate the damage and restore services. With a file integrity checker such as afick in place, when that dreaded day comes, you will be prepared to determine exactly what has happened (or is happening) and react accordingly.

In order for afick, or any other file integrity checker, to work as needed, you'll need to take special care in observing the general actions and changes in your filesystem in order to correctly and efficiently craft your configuration rules. Once you've done that, it's just a matter of staying on top of the ever-changing filesystem. Update regularly and update often, so as to catch problems as they begin, and not when it's too late. If you make these practices a regular part of your daily administration routines, you'll be prepared to react efficiently to a breach in security should the need arise.

Brian Warshawsky has built, supported, and administered mission-critical IT infastructure for the United States Naval Research Laboratory, Virginia Commonwealth University, RichmondAir Wireless, and is currently employed by Sungard Collegis at Virginia State University.

Recommended Links


In case of broken links please try to use Google search. If you find the page please notify us about new location
Google     

Nabou

Nabou Advanced Host Intrusion Detection System

Nabou is a Perl program which can be used to monitor file changes and directories on your system using MD5 checksums. It can also monitor crontab entries, suid files, user accounts, listening TCP/UDP ports, and processes. Nabou stores all data in standard dbm databases. Encrypted databases are supported using RSA public key encryption. Nabou is highly configurable; you can exclude files from being checked, configure which file attributes it should look for, use custom checks, and much more.

 

Toby IDS -- Perl reimplementation of Tripwire
(development status: stalled )

BSI Software Toby The Toby intrusion detection system is an attempt of reimplementation of tripwire-1.3 (ASR) into Perl. Probably the best free Perl integrity checker around. The current version is 1.1 (Jan-2002). It maintains a database of file properties to detect alterations. It supports MD5 and SHA-1 checksums of the file contents. It features a configuration file which is actually a Perl script, with the attendant power, flexibility, and difficulty.

Afick

freshmeat.net Project details for Another File Integrity Checker

afick is another file integrity checker, designed to be fast and fully portable between Unix and Windows platforms. It works by first creating a database that represents a snapshot of the most essential parts of your computer system. You can then run the script to discover all modifications made since the snapshot was taken (i.e. files added, changed, or removed). The configuration syntax is very close to that of aide or tripwire, and a graphical interface is provided.

 

Fcheck
(Development status: active)

fcheck is GPL licensed perl code written by Michael A. Gumienny. Its a medium size (80K source) PERL script.  It's source can be downloaded from various sites (debian). The current version is 2.7.59

Description: IDS filesystem baseline integrity checker. The fcheck utility is an IDS (Intrusion Detection System) which can be used to monitor changes to any given filesystem.
 
Essentially, fcheck has the ability to monitor directories, files or complete filesystems for any additions, deletions, and modifications.  It is configurable to exclude active log files, and can be ran as often  as needed from the command line or cron making it extremely difficult to circumvent.

Viper -- Perl script
(development status: active)

Resentment.org -- viper is small perl script that stores its database in each directory. This is a very questionable approach because it clutter the filesystem. Also if directory is writable, than deletion of the file is always possible.

Slipwire -- Perl script SHA-1 hashes, uses DBM
(Development status: stalled)

slipwire.pl is a simple filesystem integrity checker that uses Berkeley DBM. It compares the SHA-1 hashes of files to an initial state and alerts the user of any changes.Changes: A fix for a bug in the iteration count when comparing files to hashes, a quick reader script for dumping the contents of the DBM file, an example file list, and a tidied-up README.

Homepage: http://packet.node.to/
Appindex Record: http://freshmeat.net/appindex/2000/02/16/950742480.html
Author: James Quinby
License: Artistic & GPL
Category: Console/Monitoring
Depends on: Perl, Berkeley DBM, md5

Notes:
  • This is a Spartan WHYFF (We Help You For Free) site written by people for whom English is not a native language. Some amount of grammar and spelling errors should be expected.
  • The site contain some broken links as it develops like a living tree... Please try to use Google, Open directory, etc. to find a replacement link (see HOWTO search the WEB for details). We would appreciate if you can mail us a correct link.
Google Search
Open directory

Research Index



Copyright © 1996-2009 by Dr. Nikolai Bezroukov. www.softpanorama.org was created as a service to the UN Sustainable Development Networking Programme (SDNP) in the author free time. Submit comments This document is an industrial compilation designed and created exclusively for educational use and is placed under the copyright of the Open Content License(OPL). Site uses AdSense so you need to be aware of Google privacy policy. Original materials copyright belong to respective owners. Quotes are made for educational purposes only in compliance with the fair use doctrine.

Disclaimer:

Created: May 16, 1997; Last modified: August 15, 2009