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 )