#!/usr/bin/env perl # # Clubber -- Less painful chroot environment building and verification. Please # see README.markdown for documentation. Be sure you trust your binaries! This # script won't protect you from ldd exploits. # # @author Tom Ryder # @copyright 2012 Sanctum # use strict; use warnings; use Cwd qw(abs_path); use File::Basename; use Getopt::Long; # # Bail if we're not root. # if ($< != 0 || $> != 0) { error("You must have root permissions to use this script."); } # # Check ldd is available. # chomp(my $ldd = `which ldd`); if (!$ldd) { error("Couldn't find ldd in your \$PATH."); } # # Check md5sum is available. # chomp(my $md5sum = `which md5sum`); if (!$md5sum) { error("Couldn't find md5sum in your \$PATH."); } # # Check options. # my ($chroot, $dry, $verify) = ("", 0, 0, 0); my $config = GetOptions("chroot=s" => \$chroot, "dry" => \$dry); if ($chroot) { $chroot = abs_path($chroot); if (!-d $chroot) { error("Nominated chroot %s doesn't seem to be a directory.", $chroot); } } elsif ($dry) { error("Doesn't make sense to specify --dry without --chroot."); } # # Check we were passed at least one parameter, otherwise print a helpful # message. # if (!@ARGV) { printf STDOUT "USAGE: ${0} [--chroot] [--verify] [--dry] binary1 binary2 ... \n"; exit 0; } # # Check that all our parameters are real files, or point to one. # my $binaries = []; foreach my $argument (@ARGV) { $argument = abs_path($argument); if (-f $argument) { push @$binaries, $argument; } else { error("File %s doesn't seem to exist.", $argument); } } # # Run ldd on all the files and slurp all the absolute paths; put them into a # hash to keep things unique. # my $libraries = {}; foreach my $binary (@$binaries) { my $output = [qx/${ldd} ${binary}/]; foreach my $line (@$output) { if ($line =~ m#(/\S*lib\S+)#) { $libraries->{$1} = 1; } } } # # If we have a chroot, we need to figure out what libraries require importing # and which directories require creating. # if ($chroot) { my ($directories, $imports) = ({}, {}); # # First we'll recurse through the list of libraries and flag any # directories that require creation. We won't complicate things by # reproducing any symbolic link structures. # # If the directory does exist, we'll make sure the library is in place # too, and if it is that its md5sum is the same as our root system # library. If it doesn't exist or if it's different, we'll flag it for # overwriting. # foreach my $library (keys(%$libraries)) { my $directory = dirname($library); if (!-d "${chroot}${directory}") { $directories->{$directory} = 1; } if (-f "${chroot}{$library}") { if (qx/${md5sum} ${library}/ ne qx/${md5sum} ${chroot}${library}/) { $imports->{$library} = 1; } } else { $imports->{$library} = 1; } } # # Check there's something for us to do. # if (keys %$directories || keys %$imports) { # # If we're just supposed to print what we do, all the better, do # that and then quit. # if ($dry) { if (keys %$directories) { printf STDOUT "Create directories:\n"; foreach my $directory (keys(%$directories)) { printf STDOUT " %s\n", "${chroot}${directory}"; } } if (keys %$imports) { printf STDOUT "Copy libraries:\n"; foreach my $import (keys(%$imports)) { printf STDOUT " %s -> %s\n", $import, "${chroot}${import}"; } } # # Otherwise, we'd best get started. # } else { foreach my $directory (keys(%$directories)) { system("mkdir -pv ${chroot}${directory}"); } foreach my $import (keys(%$imports)) { system("cp -pv ${import} ${chroot}${import}"); } } # # If there's nothing we need to do, say so. # } else { printf STDOUT "Nothing to do.\n"; } # # If we don't have a chroot, we can just print the list of libraries, and we're # done. # } else { foreach my $library (keys(%$libraries)) { printf "%s\n", $library; } } # # And one way or another, we're done. # exit 0; # # Print a usage message and exit with non-zero. # sub usage { my $message = shift @_; printf STDERR "USAGE: ${message}\n", @_; exit 1; } # # Print a usage message and exit with non-zero. # sub error { my $message = shift @_; printf STDERR "ERROR: ${message}\n", @_; exit 1; }