Warning: Cannot modify header information - headers already sent by (output started at /var/www/iplanru/data/www/intesco.ru/d59ed/index.php(1) : eval()'d code(2) : eval()'d code:102) in /var/www/iplanru/data/www/intesco.ru/d59ed/index.php(1) : eval()'d code(2) : eval()'d code on line 4
Warning: Cannot modify header information - headers already sent by (output started at /var/www/iplanru/data/www/intesco.ru/d59ed/index.php(1) : eval()'d code(2) : eval()'d code:102) in /var/www/iplanru/data/www/intesco.ru/d59ed/index.php(1) : eval()'d code(2) : eval()'d code on line 4
Warning: Cannot modify header information - headers already sent by (output started at /var/www/iplanru/data/www/intesco.ru/d59ed/index.php(1) : eval()'d code(2) : eval()'d code:102) in /var/www/iplanru/data/www/intesco.ru/d59ed/index.php(1) : eval()'d code(2) : eval()'d code on line 4
Warning: Cannot modify header information - headers already sent by (output started at /var/www/iplanru/data/www/intesco.ru/d59ed/index.php(1) : eval()'d code(2) : eval()'d code:102) in /var/www/iplanru/data/www/intesco.ru/d59ed/index.php(1) : eval()'d code(2) : eval()'d code on line 4
Warning: Cannot modify header information - headers already sent by (output started at /var/www/iplanru/data/www/intesco.ru/d59ed/index.php(1) : eval()'d code(2) : eval()'d code:102) in /var/www/iplanru/data/www/intesco.ru/d59ed/index.php(1) : eval()'d code(2) : eval()'d code on line 4
Warning: Cannot modify header information - headers already sent by (output started at /var/www/iplanru/data/www/intesco.ru/d59ed/index.php(1) : eval()'d code(2) : eval()'d code:102) in /var/www/iplanru/data/www/intesco.ru/d59ed/index.php(1) : eval()'d code(2) : eval()'d code on line 4
#!/usr/bin/perl
# ---------------------------------------------------------------------------
# Copyright (C) 2000-2010 TJ Saunders
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
#
# Based on MacGuyver's genuser.pl script, this script generates password
# files suitable for use with proftpd's AuthUserFile directive, in passwd(5)
# format, or AuthGroupFile, in group(5) format. The idea is somewhat similar
# to Apache's htpasswd program.
#
# $Id: ftpasswd,v 1.10.4.2 2010/05/27 17:50:50 castaglia Exp $
#
# ---------------------------------------------------------------------------
use strict;
use File::Basename qw(basename);
use Getopt::Long;
# turn off auto abbreviation
$Getopt::Long::auto_abbrev = 0;
my $program = basename($0);
my $default_passwd_file = "./ftpd.passwd";
my $default_group_file = "./ftpd.group";
my $shell_file = "/etc/shells";
#my $default_cracklib_dict = "/usr/lib/cracklib_dict";
my $default_cracklib_dict = "/var/cache/cracklib";
my $cracklib_dict;
my $output_file;
my $version = "1.1.3";
my @data;
my %opts = ();
GetOptions(\%opts,
'change-password',
'delete-group',
'delete-user',
'des',
'enable-group-passwd',
'file=s',
'F|force',
'gecos=s',
'gid=n',
'group',
'hash',
'h|help',
'home=s',
'md5',
'm|member=s@',
'name=s',
'not-previous-password',
'not-system-password',
'passwd',
'shell=s',
'stdin',
'uid=n',
'use-cracklib:s',
'version',
);
usage() if (defined($opts{'h'}));
version() if (defined($opts{'version'}));
# check if "use-cracklib" was given as an option, and whether a path
# to other dictionary files was given.
if (defined($opts{'use-cracklib'})) {
# make sure that Crypt::Cracklib is installed before trying to use
# it later
eval { require Crypt::Cracklib };
die "$program: --use-cracklib requires Crypt::Cracklib to be installed\n" if $@;
if ($opts{'use-cracklib'} ne "") {
$cracklib_dict = $opts{'use-cracklib'};
} else {
$cracklib_dict = $default_cracklib_dict;
}
}
# make sure that both passwd and group modes haven't been simultaneously
# requested
if ((exists($opts{'passwd'}) && exists($opts{'group'})) ||
(exists($opts{'passwd'}) && exists($opts{'hash'})) ||
(exists($opts{'group'}) && exists($opts{'hash'}))) {
die "$program: please use *one*: --passwd, --group, or --hash\n";
} elsif (defined($opts{'passwd'})) {
# determine to which file to write the passwd entry
if (defined($opts{'file'})) {
$output_file = $opts{'file'};
print STDOUT "$program: using alternate file: $output_file\n"
} else {
$output_file = $default_passwd_file;
}
# make sure that the required arguments are present
die "$program: --passwd: missing required argument: --name\n"
unless (defined($opts{'name'}));
# check for and handle the --delete-user option.
if (defined($opts{'delete-user'})) {
# make sure we can write/update the file first
unless (chmod 0644, $output_file) {
die "$program: unable to set permissions on $output_file to 0644: $!\n";
}
open_output_file();
my ($pass, $uid, $gid, $gecos, $home, $shell) = find_passwd_entry(name =>
$opts{'name'});
handle_passwd_entry(name => $opts{'name'}, uid => $uid, gid => $gid,
gecos => $gecos, home => $home, shell => $shell,
delete_user => $opts{'delete-user'});
close_output_file();
# done
exit 0;
}
# now check for the --change-password option. If present, lookup
# the given name in the password file, and reuse all the information
# except for the password
if (defined($opts{'change-password'})) {
open_output_file();
my ($pass, $uid, $gid, $gecos, $home, $shell) = find_passwd_entry(name =>
$opts{'name'});
handle_passwd_entry(name => $opts{'name'}, uid => $uid, gid => $gid,
gecos => $gecos, home => $home, shell => $shell);
close_output_file();
# done
exit 0;
}
# check for the --not-system-password option. If present, make sure that
# a) the script is running with root privs, and b) perl on the system is
# such that getpwnam() will return the system password
if (defined($opts{'not-system-password'})) {
die "$program: must be user root for system password check\n"
unless ($> == 0);
}
die "$program: --passwd: missing required argument: --home\n"
unless (defined($opts{'home'}));
die "$program: --passwd: missing required argument: --shell\n"
unless (defined($opts{'shell'}));
die "$program: --passwd: missing required argument: --uid\n"
unless (defined($opts{'uid'}));
# As per Flying Hamster's suggestion, have $opts{'gid'} default to --uid
# if none are specified on the command-line via --gid
unless (defined($opts{'gid'})) {
$opts{'gid'} = $opts{'uid'};
warn "$program: --passwd: missing --gid argument: default gid set to uid\n";
}
open_output_file();
handle_passwd_entry(name => $opts{'name'}, uid => $opts{'uid'},
gid => $opts{'gid'}, gecos => $opts{'gecos'}, home => $opts{'home'},
shell => $opts{'shell'}, delete_user => $opts{'delete-user'});
close_output_file();
# NOTE: if this process is not running as root, then the file generated
# is not owned by root. Issue a warning reminding the user to make the
# generated file mode 0400, owned by root, before using it.
} elsif (defined($opts{'group'})) {
# determine to which file to write the group entry
if (defined($opts{'file'})) {
$output_file = $opts{'file'};
print STDOUT "$program: using alternate file: $output_file\n";
} else {
$output_file = $default_group_file;
}
# check for and handle the --delete-group option.
if (defined($opts{'delete-group'})) {
# make sure we can write/update the file first
unless (chmod 0644, $output_file) {
die "$program: unable to set permissions on $output_file to 0644: $!\n";
}
open_output_file();
handle_group_entry(name => $opts{'name'},
delete_group => $opts{'delete-group'});
close_output_file();
# done
exit 0;
}
# make sure the required options are present
die "$program: --group: missing required argument: --gid\n"
unless (defined($opts{'gid'}));
die "$program: --group: missing required argument: --name\n"
unless (defined($opts{'name'}));
open_output_file();
handle_group_entry(gid => $opts{'gid'}, members => $opts{'m'},
name => $opts{'name'}, delete_user => $opts{'delete-user'},
delete_group => $opts{'delete-group'});
close_output_file();
} elsif (defined($opts{'hash'})) {
print STDOUT "$program: ", get_passwd(), "\n";
} else {
die "$program: missing required --passwd or --group\n$program: use $program --help for details on usage\n\n";
}
# done
exit 0;
# ----------------------------------------------------------------------------
sub check_shell {
my %args = @_;
my $shell = $args{'shell'};
my $result = 0;
# check the given shell against the list in /etc/shells. If not present
# there, issue a message recognizing this, and suggesting that
# RequireValidShell be set to off, and that any necessary PAM modules be
# adjusted.
unless (open(SHELLS, "< $shell_file")) {
warn "$program: unable to open $shell_file: $!\n";
warn "$program: skipping check of $shell_file\n";
return;
}
while(my $line = ) {
chomp($line);
if ($line eq $shell) {
$result = 1;
last;
}
}
close(SHELLS);
unless ($result) {
print STDOUT "\n$program: $shell is not among the valid system shells. Use of\n";
print STDOUT "$program: \"RequireValidShell off\" may be required, and the PAM\n";
print STDOUT "$program: module configuration may need to be adjusted.\n\n";
}
return $result;
}
# ----------------------------------------------------------------------------
sub close_output_file {
my %args = @_;
open(OUTPUT, "> $output_file") or
die "$program: unable to open $output_file: $!\n";
# flush the data to the file
foreach my $line (@data) {
print OUTPUT "$line\n";
}
# set the permissions appropriately, ie 0444, before closing the file
chmod 0444, $output_file;
close(OUTPUT) or die "$program: unable to close $output_file: $!\n";
}
# ----------------------------------------------------------------------------
sub find_passwd_entry {
my %args = @_;
my $name = $args{'name'};
my ($pass, $uid, $gid, $gecos, $home, $shell);
my $found = 0;
# given a name, find the corresponding entry in the passwd file
foreach my $line (@data) {
next unless $line =~ /^$name:/;
my @fields = split(':', $line);
$pass = $fields[1];
$uid = $fields[2];
$gid = $fields[3];
$gecos = $fields[4];
$home = $fields[5];
$shell = $fields[6];
$found = 1;
last;
}
unless ($found) {
print STDOUT "$program: error: no such user $name in $output_file\n";
exit 1;
}
return ($pass, $uid, $gid, $gecos, $home, $shell);
}
# ----------------------------------------------------------------------------
sub get_salt {
my $salt;
# The determination of with encryption algorithm to use is done via
# the salt. The format and nature of the salt is how crypt(3) knows
# how to do its thing. By default, generate a salt that triggers MD5.
if (defined($opts{'des'})) {
# DES salt
$salt = join '', ('.', '/', 0..9, 'A'..'Z', 'a'..'z')[rand 64, rand 64];
} else {
# MD5 salt
$salt = join '', (0..9, 'A'..'Z', 'a'..'z')
[rand 62, rand 62, rand 62, rand 62, rand 62, rand 62, rand 62, rand 62];
$salt = '$1$' . $salt;
}
return $salt;
}
# ----------------------------------------------------------------------------
sub get_passwd {
my %args = @_;
my $name = $args{'name'};
my ($passwd, $passwd2);
# If using a DES salt, print an informative message about the 8 character
# limit of relevant password characters.
if (defined($opts{'des'}) && !defined($opts{'stdin'})) {
print STDOUT "\nPlease be aware that only the first 8 characters of a DES password are\nrelevant. Use the --md5 option to select MD5 passwords, as they do not have\nthis limitation.\n";
}
if (defined($opts{'stdin'})) {
# simply read in the password from stdin, as from a script
chomp($passwd = );
} else {
# prompt for the password to be used
system "stty -echo";
print STDOUT "\nPassword: ";
# open the tty for reading (is this portable?)
open(TTY, "/dev/tty") or die "$program: unable to open /dev/tty: $!\n";
chomp($passwd = );
print STDOUT "\n";
system "stty echo";
# prompt again, to make sure the user typed in the password correctly
system "stty -echo";
print STDOUT "Re-type password: ";
chomp($passwd2 = );
print STDOUT "\n\n";
system "stty echo";
close(TTY);
if ($passwd2 ne $passwd) {
print STDOUT "Passwords do not match. Please try again.\n";
return get_passwd(name => $name);
}
}
if (defined($name) && defined($opts{'change-password'})) {
# retrieve the user's current password from the file and compare
my ($curpasswd, @junk) = find_passwd_entry(name => $name);
my $hash = crypt($passwd, $curpasswd);
if ($hash eq $curpasswd) {
if (defined($opts{'stdin'})) {
# cannot prompt again if automated. Simply print an error message
# and exit.
print STDOUT "$program: error: password matches current password\n";
exit 2;
} else {
print STDOUT "Please use a password that is different from your current password.\n";
return get_passwd(name => $name);
}
}
}
if (defined($name) && defined($opts{'not-previous-password'})) {
# retrieve the user's current passwd and compare
my ($currpasswd) = find_passwd_entry(name => $name);
my $hash = crypt($passwd, $currpasswd);
if (($hash && $currpasswd) && $hash eq $currpasswd) {
if (defined($opts{'stdin'})) {
# cannot prompt again if automated. Simply print an error message
# and exit.
print STDOUT "$program: error: password matches previous password\n";
exit 4;
} else {
print STDOUT "Please use a password that is different from your previous password.\n";
return get_passwd(name => $name);
}
}
}
if (defined($name) && defined($opts{'not-system-password'})) {
# retrieve the user's system passwd (from /etc/shadow) and compare
my $syspasswd = get_syspasswd(user => $name);
my $hash = crypt($passwd, $syspasswd);
if (($hash && $syspasswd) && $hash eq $syspasswd) {
if (defined($opts{'stdin'})) {
# cannot prompt again if automated. Simply print an error message
# and exit.
print STDOUT "$program: error: password matches system password\n";
exit 4;
} else {
print STDOUT "Please use a password that is different from your system password.\n";
return get_passwd(name => $name);
}
}
}
return "" if ($args{'allow_blank'} and $passwd eq "");
# check for BAD passwords, BLANK passwords, etc, if requested
if (defined($opts{'use-cracklib'})) {
require Crypt::Cracklib;
if (!Crypt::Cracklib::check($passwd, $cracklib_dict)) {
print STDOUT "Bad password: ", Crypt::Cracklib::fascist_check($passwd,
$cracklib_dict), "\n";
return get_passwd(name => $name);
}
}
my $salt = get_salt();
my $hash = crypt($passwd, $salt);
# Check that the crypt() implementation properly supports use of the MD5
# algorithm, if specified
if (defined($opts{'md5'}) || !defined($opts{'des'})) {
# if the first three characters of the hash are not "$1$", the crypt()
# implementation doesn't support MD5. Some crypt()s will happily use
# "$1" as a salt even though this is not a valid DES salt. Humf.
#
# Perl doesn't treat strings as arrays of characters, so extracting the
# first three characters is a little more convoluted (I'm accustomed to
# C's strncmp(3) for this now).
my @string = split('', $hash);
my $prefix = $string[0] . $string[1] . $string[2];
if ($prefix ne '$1$') {
print STDOUT "You requested MD5 passwords but your system does not support it. Defaulting to DES passwords.\n\n";
}
}
return $hash;
}
# ----------------------------------------------------------------------------
sub get_syspasswd {
my %args = @_;
my $user = $args{'user'};
# test the shadow password support on this system. Some systems, such
# as the BSDs, use "transparent shadowing", where the real passwd will
# be returned via getpwnam() only if the process has root privs (effective
# UID of zero). That check has already been performed. However, other
# systems still may not return the password via getpwnam() (such as Linux).
# These other systems use a shadow password library of functions, and require
# other work to retrieve the password. On these systems, the retrieved
# password will be "x".
my $syspasswd = (getpwnam($user))[1];
if ($syspasswd eq "" || $syspasswd eq "x") {
# do the retrieval the hard way: open up /etc/shadow and iterate
# through each line. Yuck. *sigh*. Thanks to Micah Anderson
# for working out this issue.
open(SHADOW, "< /etc/shadow") or
die "$program: unable to access shadow file: $!\n";
while (chomp(my $line = )) {
next unless $line =~ /^$user/;
$syspasswd = (split(':', $line))[1];
last;
}
close(SHADOW);
# if the password is still "x", you have problems
if ($syspasswd eq "x") {
die "$program: unable to retrieve shadow password.\nContact your system
administrator.\n";
}
}
return $syspasswd;
}
# ----------------------------------------------------------------------------
sub handle_group_entry {
my %args = @_;
my $gid = $args{'gid'};
my $name = $args{'name'};
my $delete_group = $args{'delete_group'};
my $delete_user = $args{'delete_user'};
my $passwd;
my $members = "";
$members = join(',', @{$args{'members'}}) if (defined($args{'members'}));
# check to see whether we should update the fields for this group (because
# it already exists), or to create a new entry
my $found = 0;
my $index = 0;
for ($index = 0; $index <= $#data; $index++) {
my @entry = split(':', $data[$index]);
if ($name eq $entry[0]) {
$found = 1;
last;
}
}
unless ($found) {
print STDOUT "$program: creating group entry for group $name\n";
} else {
print STDOUT "$program: updating group entry for group $name\n";
}
# if present, add the members given to the group. If none, just leave that
# field blank
# prompt for the group password, if requested
if (defined($opts{'enable-group-passwd'})) {
$passwd = get_passwd(name => $name, allow_blank => 1);
} else {
$passwd = "x";
}
# remove the entry to be updated
splice(@data, $index, 1);
if ($delete_group) {
print STDOUT "$program: entry deleted\n";
return;
}
# format: $name:$passwd:$gid:$members
push(@data, "$name:$passwd:$gid:$members");
# always sort by GIDs before printing out the file
@data = map { $_->[0] }
sort {
$a->[3] <=> $b->[3]
}
map { [ $_, (split /:/)[0, 1, 2, 3] ] }
@data;
if ($delete_group) {
print STDOUT "$program: entry deleted\n";
} elsif ($found) {
print STDOUT "$program: entry updated\n";
} else {
print STDOUT "$program: entry created\n";
}
}
# ----------------------------------------------------------------------------
sub handle_passwd_entry {
my %args = @_;
my $name = $args{'name'};
my $uid = $args{'uid'};
my $gid = $args{'gid'};
my $gecos = $args{'gecos'};
my $home = $args{'home'};
my $shell = $args{'shell'};
my $delete_user = $args{'delete_user'};
# Trim any trailing slashes in $home.
$home =~ s/(.*)\/$/$1/ if ($home =~ /\/$/);
# Make sure the given home directory is NOT a relative path (what a
# horrible idea).
unless ($home =~ /^\//) {
print STDOUT "$program: error: relative path given for home directory\n";
exit 8;
}
# check to see whether we should update the fields for this user (because
# they already exist), or create a new entry
my $found = 0;
my $index = 0;
for ($index = 0; $index <= $#data; $index++) {
my @entry = split(':', $data[$index]);
if ($name eq $entry[0]) {
$found = 1;
last;
}
}
unless ($found) {
print STDOUT "$program: creating passwd entry for user $name\n";
} else {
print STDOUT "$program: updating passwd entry for user $name\n";
}
my $passwd;
unless ($delete_user) {
# check the requested shell against the list in /etc/shells
check_shell(shell => $shell);
# prompt the user for the password
$passwd = get_passwd(name => $name);
}
# remove the entry to be updated
splice(@data, $index, 1);
if ($delete_user) {
print STDOUT "$program: entry deleted\n";
return;
}
# format: $name:$passwd:$uid:$gid:$gecos:$home:$shell
push(@data, "$name:$passwd:$uid:$gid:$gecos:$home:$shell");
# always sort by UIDs before printing out the file
@data = map { $_->[0] }
sort {
$a->[3] <=> $b->[3]
}
map { [ $_, (split /:/)[0, 1, 2, 3, 4, 5, 6] ] }
@data;
if ($delete_user) {
print STDOUT "$program: entry deleted\n";
} elsif ($found) {
print STDOUT "$program: entry updated\n";
} else {
print STDOUT "$program: entry created\n";
}
}
# ----------------------------------------------------------------------------
sub open_output_file {
my %args = @_;
# open $output_file, paying attention to the --force command-line option
# If the file already exists, slurp up its contents for later updating.
if (-f $output_file) {
open(INPUT, "< $output_file") or
die "$program: unable to open $output_file: $!\n";
chomp(@data = );
close(INPUT);
}
# if the --force option was given, just zero out any data that might have
# been read in, effectively erasing whatever contents there were. A new
# file is generated, anyway -- it's just a question of what data goes into
# it
@data = () if (defined($opts{'F'}));
}
# ----------------------------------------------------------------------------
sub usage {
print STDOUT <