View Source Code
#!/usr/bin/perl
##############################################################################
# Linux Genuine Advantage #
# #
# http://www.linuxgenuineadvantage.org #
# #
# Copyright (C) 2007 Linux Genuine Advantage #
# #
# This program makes sure that a given copy of Linux is Genuine. This is #
# determined by a remote server on the internet. If money has been paid to #
# the authors of this program, then the remote server will confer the #
# Advantage of allowing the computer running this program to continue #
# functioning properly, in exactly the way it would have before this program #
# was installed. #
# #
# linux-genuine-advantage comes with ABSOLUTELY NO WARRANTY. This is free #
# software, and you may copy, distribute and/or modify it under the terms of #
# the GNU GPL (version 2 or at your option any later version). #
# See the GNU General Public License (in file: COPYING) for details. #
##############################################################################
########################################
# STANDARD MODULES #
########################################
# 'use warnings' will not work in the earliest versions of perl 5
# users with these systems will not be able to benefit from the
# Genuine Linux Advantage
use strict;
use warnings;
use CGI qw( escape );
use IO::Socket::INET;
########################################
# DECLARE VARIABLES #
########################################
my $PROGRAM_NAME = 'linux-genuine-advantage';
my $VERSION = '1.0.0';
# operational mode
my $cmd = undef;
# config directory and product key locations
my $config_directory = '/etc/linux-genuine-advantage';
my $config_key_file = $config_directory . '/product-key';
# key system files
my $inittab = '/etc/inittab';
my $nologin = '/etc/nologin';
# Domain and URL to send product key and hardware hash to. From here we receive marching
# orders in the form of a "OK" or "FAIL" string.
my $verify_domain = 'www.linuxgenuineadvantage.org';
my $verify_url = '/verify/';
# we'll be nice and wait this many days before making the user's computer
# less useful.
my $grace_period = 30;
# wait this long (in hours) before receiving remote instructions from an entity
# who doesn't have our best interests at heart.
my $phone_home_hour_interval = 24 * 14;
########################################
# CORE PROGRAM STRUCTURE #
########################################
if (! defined($ARGV[0])) {
show_usage();
}
# we have to be root to provide the Advantage
check_root();
# figure out which mode to run in
$cmd = $ARGV[0];
if ('register' eq $cmd) {
do_register();
} elsif ('install' eq $cmd) {
do_install();
} elsif ('daemon' eq $cmd) {
do_daemon();
} else {
show_usage();
}
# if we got here, it must have worked
exit (0);
########################################
# SUBROUTINES #
########################################
# shows usage information and then exits
#
# accepts no arguments
# exits the program with an error condition
sub show_usage {
print STDERR "$PROGRAM_NAME version $VERSION\n";
print STDERR "Copyright (C) 2007 Linux Genuine Advantage\n";
print STDERR "<http://www.linuxgenuineadvantage.org/>\n";
print STDERR "\n";
print STDERR "$PROGRAM_NAME comes with ABSOLUTELY NO WARRANTY. This is free\n";
print STDERR "software, and you are welcome to redistribute it under certain\n";
print STDERR "conditions. See the GNU General Public License for details.\n";
print STDERR "\n";
print STDERR "Usage: $PROGRAM_NAME [register|install]\n";
print STDERR "\n";
print STDERR " install\n";
print STDERR "\n";
print STDERR " Installs the $PROGRAM_NAME service. This is required in\n";
print STDERR " order to get the Genuine Linux Advantage.\n";
print STDERR "\n";
print STDERR " register\n";
print STDERR "\n";
print STDERR " Prints out instructions on registering your copy of Linux,\n";
print STDERR " so you can receive all the benefits of the Linux Genuine\n";
print STDERR " Advantage program.\n";
print STDERR "\n";
exit (1);
}
# shows an error message and then exits
#
# accepts an error string
# exits the program with an error condition
sub show_error {
my $str = shift(@_);
print STDERR $PROGRAM_NAME;
if (defined($str)) {
print STDERR ': ' . $str;
}
print STDERR "\n";
exit (1);
}
# shows registration information, including where the user can send the money
# to to make their copy of Linux Genuine (and thereby conferring an Advantage).
#
# accepts no arguments
sub do_register {
my $md5sum = calculate_hardware_hash();
do_install();
print "\n";
print "------------------------------------------------------------------------------\n";
print "Linux Genuine Advantage Registration\n";
print "------------------------------------------------------------------------------\n";
print "\n";
print "To benefit from the Advantage of Genuine Linux, you must fill out this form\n";
print "and send this information, along with your yearly subscription payment*, to\n";
print "http://www.linuxgenuineadvantage.org/\n";
print "\n";
print "You have $grace_period days to complete this registration. If this computer is\n";
print "not properly registered by the end of the grace period, it will enter\n";
print "Reduced Functionality Mode. Reduced Functionality Mode will work just like\n";
print "your computer did before, except you will no longer be able to log in.\n";
print "\n";
print "------------------------------------------------------------------------------\n";
print "First Name: ________________________________________________________\n";
print "Last Name: ________________________________________________________\n";
print "Company: ________________________________________________________\n";
print "E-mail Address: ________________________________________________________\n";
print "Work Phone: ________________________________________________________\n";
print "Home Phone: ________________________________________________________\n";
print "\n";
print "License Type: [ ] Single Machine (\$99 per year)\n";
print " [ ] Site License (unlimited machines) (\$999 per year)\n";
print "\n";
print "Hardware fingerprint: ", $md5sum, "\n";
print "------------------------------------------------------------------------------\n";
print "\n";
print "*Prices and terms subject to change without notice.\n";
print "\n";
}
# adds a line to /etc/inittab so this program will perpetually run as a daemon.
# this will ensure that this copy of Linux is validated for Genuineness and Advantageousness constantly.
#
# accepts no arguments
# returns 1 on success, exits the program on error
sub do_install {
my @lines = ();
my $result = undef;
my $installed = 0;
open(INITTAB, "$inittab") or show_error("Could not open $inittab for reading!");
@lines = <INITTAB>;
close(INITTAB);
if (0 == @lines) {
show_error("Could not read $inittab!");
}
# see if it's installed
foreach my $line (@lines) {
if ($line =~ m/linux\-genuine\-advantage/o) {
if ($line !~ m/#/o) {
$installed = 1;
last;
}
}
}
# if it's not installed, install it
if (0 == $installed) {
# add this program to inittab
# /usr/local/sbin/linux-genuine-advantage is the only Genuine path to this program, so no auto-detection is necessary
open(INITTAB, ">> $inittab") or show_error("Could not install into $inittab!");
print INITTAB "\n";
print INITTAB "# Linux Genuine Advantage - http://www.linuxgenuineadvantage.org/\n";
print INITTAB "LGA:123456:respawn:/usr/local/sbin/$PROGRAM_NAME daemon\n";
close(INITTAB);
# restart init (kill -HUP 1)
$result = kill(1, 1);
if (! $result) {
show_error("Could not restart init!");
}
}
return (1);
}
# loop continuously, checking periodically to see if the author of this program got paid.
# if no money has changed hands after 30 days, make the computer less useful as punishment.
#
# accepts no arguments
# runs as a daemon forever (called from inittab, does not fork into the background)
sub do_daemon {
my $result = undef;
# assume the user is a criminal
my $is_genuine = 0;
# check immediately at startup (once we're outside the grace period), then reset the counter later
my $hours_since_phoning_home = 0;
$result = create_config_dir();
if (! defined($result)) {
show_error("Could not create config directory and associated files... Linux can not be Genuine without them!");
}
# main daemon loop
while (1) {
# if we're past the grace period, start the checks...
if ( grace_period_expired() ) {
# has it been long enough since we last phoned home?
# (do it every hour if the machine is not genuine, so it won't take two weeks or a reboot to fix)
if (($hours_since_phoning_home > $phone_home_hour_interval) or (! $is_genuine)) {
# contact the home base and see if they will allow our computer to continue functioning
$is_genuine = phone_home();
# reset the ticking clock
$hours_since_phoning_home = 0;
# count the time until we get remote instructions from a computer who has power over us again
} else {
$hours_since_phoning_home++;
}
# the remote unaccountable third party has decided to let our computer continue functioning
# (until the next check two weeks from now)
if ($is_genuine) {
# make this computer as useful as it was by default
disable_reduced_functionality();
# log to syslog
system("logger -i -p user.info -t $PROGRAM_NAME This copy of Linux is Genuine. See http://www.linuxgenuineadvantage.org for details.");
# the remote unaccountable third party has decided our computer isn't Genuine
} else {
# make this computer less useful
enable_reduced_functionality();
# log to syslog
system("logger -i -p user.info -t $PROGRAM_NAME This copy of Linux is not Genuine. Running in reduced functionality mode. See http://www.linuxgenuineadvantage.org for details.");
}
}
# sleep for one hour
sleep(3600);
}
}
# creates the config directory where we store our product key (if it doesn't exist already).
#
# accepts no arguments
# returns 1 on success, undef on failure
sub create_config_dir {
my $result = undef;
# create the directory (if it doesn't exist)
if (! -e "$config_directory") {
$result = mkdir($config_directory);
if (! defined($result)) { return (undef); }
}
# create the key file (if it doesn't exist)
if (! -e "$config_key_file") {
$result = open(KEY, "> $config_key_file");
if (! defined($result)) { return (undef); }
close(KEY);
}
return (1);
}
# calculates an md5sum based on the current hardware configuration
# if any of this computer's hardware changes, so will the hash (requiring re-registration).
# if the hardware changes too many times, that would look suspicious, so the fee might have to be paid again
# (at the sole discretion of linuxgenuineadvantage.org, of course).
#
# accepts no arguments
# returns an md5sum that will cause unnecessary grief for the user if it doesn't match exactly
sub calculate_hardware_hash {
my $md5sum = `(/sbin/ifconfig | grep -i hwaddr | awk '{print \$5}' | sort -u; cat /proc/cpuinfo | egrep -i 'processor|vendor_id|cpu family|model|model name|stepping') | md5sum | awk '{print \$1}'`;
chomp($md5sum);
return ($md5sum);
}
# enables "reduced functionality" mode
# this feature prohibits logins to the computer, as an incentive to the system administrator to make sure
# their copy of Linux is Genuine.
#
# accepts no arguments
# returns 1 if we successfully rendered this computer less useful, or undef if the computer is still working properly
sub enable_reduced_functionality {
my $result = undef;
$result = open(NOLOGIN, "> $nologin");
if (! defined($result)) { return (undef); }
print NOLOGIN "\n";
print NOLOGIN "Linux Genuine Advantage - Reduced Functionality Mode\n";
print NOLOGIN "\n";
print NOLOGIN "Logins currently disabled, see\n";
print NOLOGIN "http://www.linuxgenuineadvantage.org for licensing options.\n";
print NOLOGIN "\n";
close(NOLOGIN);
return (1);
}
# disables "reduced functionality" mode
#
# accepts no arguments
# returns 1 if we successfully turned off the reduced functionality mode "feature", or undef if we were unable too
sub disable_reduced_functionality {
my $result = undef;
if ( -e "$nologin" ) {
$result = unlink("$nologin");
if (! defined($result)) { return (undef); }
}
return (1);
}
# checks to see if the user is root or not
#
# accepts no arguments
# returns 1 if the user is root, or exits with an error condition if they're not.
sub check_root {
if (0 != $<) {
show_error("This program can only provide a Genuine Advantage as the root user.");
}
return (1);
}
# checks to see if we're past the grace period (as defined by the mtime of
# the product_key file, an infalliable strategy)
#
# accepts no arguments
# returns 1 if we're past the grace period, and 0 if we're within the grace period
sub grace_period_expired {
my @data = stat($config_key_file);
# if the config directory is older than $grace_period days, we're outside the grace period
if (@data && $data[9]) {
if ($data[9] > (time() - ($grace_period * 24 * 60 * 60))) {
# we're still in the grace period, for now
return (0);
}
}
# we're past the grace period. this most likely means the user is a criminal.
return (1);
}
# contacts a remote, third party server on the internet to determine if the computer is allowed to function normally
#
# accepts no arguments
# returns 1 if the computer is allowed to function, or 0 if people you've never met decided it should be artificially crippled
sub phone_home {
my $result = undef;
my $buf = undef;
my @lines = ();
my $past_headers = 0;
# get hardware hash
my $hardware_hash = calculate_hardware_hash();
# get product key
my $product_key = get_product_key();
my $sock = IO::Socket::INET->new(
PeerAddr => $verify_domain,
PeerPort => 'http(80)',
Proto => 'tcp'
);
$result = $sock->send("GET $verify_url?h=" . escape($hardware_hash) . "&k=" . escape($product_key) . " HTTP/1.1\nHost: $verify_domain\nUser-Agent: $PROGRAM_NAME/$VERSION\n\n");
if (! defined($result)) { return (1); }
$result = $sock->recv($buf, 4096);
if (! defined($result)) { return (1); }
@lines = split(/\n/, $buf);
foreach my $line (@lines) {
$line =~ s/\r//go;
$line =~ s/\n//go;
# we found the blank line separating the headers from the body
if ($line eq '') {
$past_headers = 1;
}
# if we're looking at the message body, look for either 'OK' or 'FAIL' on a line by itself
if ($past_headers) {
# OK
if ($line =~ m/^OK$/o) {
return (1);
}
# FAIL
if ($line =~ m/^FAIL$/o) {
return (0);
}
}
}
# err on the side of caution (we can always change this without notification in a future release)
return (1);
}
# gets the product key, by reading it from a file
#
# accepts no arguments
# returns the product key string (or a blank string, if not found)
sub get_product_key {
my $product_key = undef;
my $result = undef;
if ( -r "$config_key_file" ) {
$result = open(FILE, "$config_key_file");
if ($result) {
$product_key = <FILE>;
close(FILE);
chomp($product_key);
}
if ($product_key) {
return $product_key;
}
}
return '';
}
© 2007 Linux Genuine Advantage