Cisco-Conf
view release on metacpan or search on metacpan
lib/Cisco/Conf.pm view on Meta::CPAN
so that we can change the EUID to the users.
Example:
$self->Edit('emacs', 'myrouter.conf', '/tmp');
=cut
sub _System($$) {
my($class, $command) = @_;
$! = 0;
my $rc = system $command;
if ($rc == 0xff00) {
die "Command $command failed: " .
($! || "Unknown system error");
} elsif ($rc) {
die "Command $command exited, error status $rc";
}
}
sub _Edit ($$$$) {
my($self, $editor, $file, $tmpDir) = @_;
if ($< == $>) {
# We aren't running SUID, so things are easy.
return $self->_System(sprintf("%s %s", $editor, $file));
}
# Editing a file is a true security problem. :-(
# Most editors have escape sequences that allow to execute
# arbitrary shell commands with. When running suid root,
# this means that we can do just anything!
#
# We try to work around this problem as follows:
# First we create a copy of the file that should be
# edited. The real user becomes owner of this file.
#
# Next we fork a child. This child changes the EUID
# to the UID, so it is no longer running suid. Now
# we can edit the copy.
#
# Finally the copy is restored back to become the original.
# That's it, folks! :-)
#
my $configuration;
my $fh;
{
local $/ = undef;
$fh = IO::File->new($file, "r");
if (!$fh || !defined($configuration = $fh->getline()) ||
!$fh->close()) {
die "Error while reading $file: $!";
}
}
my $tmpFile = $tmpDir . "/cisconf.$$";
$fh = IO::File->new($tmpFile, "w");
if (!$fh || !chmod(0600, $tmpFile) ||
!chown($<, $(, $tmpFile) || !$fh->print($configuration) ||
!$fh->flush() || !$fh->close()) {
unlink $tmpFile;
die "Error while creating temporary file $tmpFile: $!";
}
my $pid = fork();
if (!defined($pid)) {
unlink $tmpFile;
die "Cannot fork: $!";
} elsif (!$pid) {
# This is the child; change UID and call the editor
$) = $(;
$> = $<;
exec sprintf("%s %s", $editor, $tmpFile);
}
{
local($SIG{'CHLD'}) = 'IGNORE';
wait;
if ($?) {
unlink $tmpFile;
die "Error while editing $tmpFile, error status was $?";
}
}
# Now copy the temporary file back
{
local $/ = undef;
$fh = IO::File->new($tmpFile, "r");
if (!$fh || !defined($configuration = $fh->getline()) ||
!$fh->close()) {
my $status = $!;
unlink $tmpFile;
die "Error while reading $tmpFile: $status";
}
}
unlink $tmpFile;
$fh = IO::File->new("$file.new", "w");
if (!$fh || !$fh->print($configuration) || !$fh->flush() ||
!$fh->close()) {
my $status = $!;
die "Error while creating new file $file.new: $status";
}
if (-f $file) {
unlink "$file.bak";
if (!rename $file, "$file.bak") {
die "Error while renaming $file to $file.bak: $!";
}
}
if (!rename "$file.new", $file) {
die "Error while renaming $file.new to $file: $!";
}
}
sub Edit ($$$$) {
my($self, $editor, $file, $tmpDir) = @_;
lib/Cisco/Conf.pm view on Meta::CPAN
my $match;
(undef, $match) = $cmd->waitfor(Match => '/\>/',
Match => '/\#/',
Match => '/ogin:/',
Match => '/sername:/',
Match => '/assword:/');
if ($match =~ /\#/) {
if (!$cmd->print("term mon")) {
die "Output error: $!";
}
$cmd->waitfor('/\#/');
return $cmd;
}
my $output;
if ($match =~ /\>/) {
$loggedIn = 1;
$output = "enable";
} elsif ($match =~ /sername:/ || $match =~ /ogin:/) {
$output = $self->{'username'} ||
$self->_PromptPassword("Username");
} elsif (!$loggedIn) {
$output = $self->{'password'} ||
$self->_PromptPassword("Login");
} else {
$output = $self->{'enable_password'} ||
$self->_PromptPassword("Enable");
}
if (!$cmd->print($output)) {
die "Output error: $!";
}
}
}
sub _Logout ($$) {
my($self, $cmd) = @_;
if (!$cmd->print("logout")) {
die "Output error: $!";
}
$cmd->close();
print "\n";
}
sub Load ($$) {
my($self, $file) = @_;
my $tftp_client_file = $file;
if ($self->{'tftp_prefix'}) {
my $prefix = $self->{'tftp_prefix'};
if ($tftp_client_file =~ /^\Q$prefix\E(.*)/) {
$tftp_client_file = $1;
} else {
print STDERR("Warning: TFTP prefix $prefix doesn't match file",
" name $tftp_client_file.\n");
}
}
# Create an empty file $file
my $fh;
if (!($fh = IO::File->new($file, "w")) || !$fh->close() ||
!chmod(0666, $file)) {
die "Cannot create $file: $!";
}
my $cmd = $self->_Login();
if (!$cmd->print("copy running-config tftp")) {
die "Output error: $!";
}
$cmd->waitfor('/\[(\d+\.\d+\.\d+\.\d+)?\]\? /');
if (!$cmd->print($self->{'_local_addr'})) {
die "Output error: $!";
}
my($prematch, $match) =
$cmd->waitfor('/(destination filename|file to write)\s+\[.*\]\? /i');
if (!$cmd->print($tftp_client_file)) {
die "Output error: $!";
}
if ($match =~ /^destination/i) {
# Cisco IOS 12.0
$cmd->waitfor('/\d+\s+bytes\s+copied\s+in/');
} else {
$cmd->waitfor('/\[confirm\]/');
if (!$cmd->print('y')) {
die "Output error: $!";
}
$cmd->waitfor('/\[OK\].*\#/s');
}
$self->_Logout($cmd);
}
=pod
=head2 Save($file, $write)
(Instance method) Reads a machines configuration from $file and save it
into the router. Like with the I<Load> method, possible locations of
$file depend on your TFTP servers settings.
Note that the file mode of $file will be changed to 0444, on other words,
the file is readable for the world! You should change this as soon as
possible.
If the argument $write is TRUE, the configuration will be saved into
the non-volatile memory by executing the command
write memory
=cut
sub Save ($$;$) {
my($self, $file, $write) = @_;
my $tftp_client_file = $file;
if ($self->{'tftp_prefix'}) {
my $prefix = $self->{'tftp_prefix'};
if ($tftp_client_file =~ /^\Q$prefix\E(.*)/) {
$tftp_client_file = $1;
} else {
print STDERR("Warning: TFTP prefix $prefix doesn't match file",
" name $tftp_client_file.\n");
}
}
# Change the file permissions of $file to 0444, so that it's
# readable by the TFTP server
if (!chmod(0444, $file)) {
die "Cannot make $file readable: $!";
}
my $cmd = $self->_Login();
if (!$cmd->print("copy tftp running-config")) {
die "Output error: $!";
}
my($prematch, $match) =
$cmd->waitfor('/\[(host|\d+\.\d+\.\d+\.\d+)?\]\? /');
if ($match eq "[host]") {
# Cisco IOS below 12.0
if (!$cmd->print("")) {
die "Output error: $!";
}
$cmd->waitfor('/\[(\d+\.\d+\.\d+\.\d+)?\]\? /');
}
if (!$cmd->print($self->{'_local_addr'})) {
die "Output error: $!";
}
$cmd->waitfor('/(?:Source filename|Name of configuration file) \[.*\]\? /');
if (!$cmd->print($tftp_client_file)) {
die "Output error: $!";
}
($prematch, $match) =
$cmd->waitfor('/(Destination filename \[.*\]|confirm)/');
if ($match =~ /^Destination filename \[.*\]$/) {
# Cisco IOS 12.0
if (!$cmd->print("")) {
die "Output error: $!";
}
} else {
if (!$cmd->print('y')) {
die "Output error: $!";
}
}
$cmd->waitfor('/\[OK.*bytes\].*\#/s');
if ($write) {
if (!$cmd->print('write memory')) {
die "Output error: $!";
}
$cmd->waitfor('/\#/s');
}
$self->_Logout($cmd);
}
=pod
=head2 Info($configFile)
(Class method) Read a list of all configurations in C<$configFile> and
return those configurations that are accessible by the current user.
=cut
sub Info ($$) {
my($class, $configFile) = @_;
my $config = $class->_ReadConfigFile($configFile);
my ($name, @list);
( run in 0.625 second using v1.01-cache-2.11-cpan-cdf2f3d4e48 )