123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197 |
- #!/usr/bin/perl
- ### v.2.0.2 by zio 06/4/2012
- ### v.3.0.0 by gine 11/10/2015
- ### v.3.0.1 by gine 22/10/2015
- ### uses
- #
- use DBI;
- #use Crypt::Random;
- #use Crypt::Passwd::XS qw(unix_sha256_crypt);
- #use Crypt::Passwd::XS qw(unix_md5_crypt);
- use Crypt::Passwd::XS; ### The blowfish crypt() scheme is currently unsupported by Crypt::Passwd::XS::crypt() ,
- use Crypt::Eksblowfish::Bcrypt qw(bcrypt); ### so additional Crypt::Eksblowfish::Bcrypt::bcrypt() is being used
- use Digest::SHA qw(sha256_hex);
- use Log::Log4perl qw(get_logger :levels);
- ### conf
- #
- $SCRIPT_DIR="/usr/local/ortiche/pureftpd-auth";
- $AUTH_DIR="$SCRIPT_DIR/auth";
- require "$AUTH_DIR/pureftpd.pl";
- $AUTH_LOG="/var/log/pure-ftpd/auth.log"; ### log kept for debug, to remove in future because of privacy !!!
- $SHA256_DEFAULT_ROUNDS = 5000;
- ### variables read from environment ( pure-authd )
- #
- $REQ_USER=$ENV{AUTHD_ACCOUNT}; ### TODO: sanitize input ?
- $REQ_PASS=$ENV{AUTHD_PASSWORD}; #
- $SERVER_IP=$ENV{AUTHD_LOCAL_IP};
- #AUTHD_LOCAL_PORT
- #AUTHD_REMOTE_IP
- #AUTHD_ENCRYPTED
- @numbers = split(/\./, $SERVER_IP);
- $ip_number = pack("C4", @numbers);
- ($server_name) = (gethostbyaddr($ip_number, 2))[0];
- if (not $server_name)
- {
- $server_name=$SERVER_IP;
- }
- ### variables shared with subs
- #
- my $DB_conn;
- my $auth_logger;
- ### always returns with sub auth_return() ...
- #
- sub auth_return
- {
- my ( $RET_AUTH, $RET_DIR ) = @_;
- $auth_logger->info("auth_ok:", $RET_AUTH, " , reason: auth terminated, user: ", $REQ_USER);
- if ( $RET_AUTH == 1 ) ### add extra checks on fields...
- {
- print "auth_ok:1\n";
- # print "uid:", $RET_UID, "\n"; #### TODO: fix DB and user creation scripts instead!!!
- # print "gid:", $RET_GID, "\n"; ##
- print "uid:33\n"; ##
- print "gid:33\n"; ##
- print "dir:", $RET_DIR, "\n";
- print "end\n";
- exit 0;
- }
- else
- {
- print "auth_ok:", $RET_AUTH, "\n";
- print "end\n";
- exit 1;
- }
- }
- ### logging subs
- #
- sub auth_log_init
- {
- # log4perl levels:
- # DEBUG INFO WARN ERROR FATAL
- Log::Log4perl->easy_init({
- file => ">> $AUTH_LOG",
- level => $INFO,
- layout => "[3.0.1] [%d{yy/MM/dd-HH:mm}] [%p] (%F{2} line %L) %m%n",
- },{
- file => "STDERR",
- level => $DEBUG,
- layout => "[3.0.1] [%d{yy/MM/dd-HH:mm}] [%p] (%F{2} line %L) %m%n",
- });
- $auth_logger = Log::Log4perl->get_logger();
- }
- ### digests, salts and crypt() subs
- #
- sub check_crypt {
- my ( $plain_password, $hashed_password ) = @_;
- my $encrypted;
- if ( $hashed_password =~ /(\$2a\$[0-9]{2}\$[A-Za-z0-9.\/]{22})/ ) {
- $encrypted = Crypt::Eksblowfish::Bcrypt::bcrypt($plain_password, $hashed_password); ### Blowfish scheme
- } else {
- $encrypted = Crypt::Passwd::XS::crypt($plain_password, $hashed_password); ### MD5, SHA256 schemes
- }
- return ( $encrypted eq $hashed_password );
- }
- sub sha256_salt {
- # my $salt = en_base64(Crypt::Random::makerandom_octet(Length=>16));
- my @letters = ('A' .. 'Z', 'a' .. 'z', '0' .. '9', '/', '.');
- my $salt;
- for (1 .. 16) { $salt .= $letters[rand@letters]; }
- return '$5$'.'rounds='.$SHA256_DEFAULT_ROUNDS.'$'.$salt;
- }
- #sub migra_update_pw {
- # my ( $req_user, $req_pass ) = @_;
- # my $p_sha256 = sha256_hex($req_pass);
- # my $p_sha256_crypt_salt = Crypt::Passwd::XS::crypt($req_pass, sha256_salt());
- # my $UPD_QUERY = "UPDATE $DB_TABLE SET password = NULL, password_sha256 = '$p_sha256', password_crypt = '$p_sha256_crypt_salt' WHERE username = '$req_user'";
- # $auth_logger->info("updated password for hrd style: ".$REQ_USER);
- # return $DB_conn->do($UPD_QUERY);
- #}
- #deprecated
- sub migra_pw {
- my ( $req_user, $req_pass ) = @_;
- my $p_sha256_crypt_salt = Crypt::Passwd::XS::crypt($req_pass, sha256_salt());
- $auth_logger->info("going to run password migration, user: ".$REQ_USER);
- my $UPD_QUERY = "UPDATE $DB_TABLE SET password = NULL, password_crypt = '$p_sha256_crypt_salt' WHERE username = '$req_user'";
- return $DB_conn->do($UPD_QUERY);
- }
- sub update_hrd_pw {
- my ( $req_user, $req_pass ) = @_;
- my $p_sha256 = sha256_hex($req_pass);
- my $UPD_QUERY = "UPDATE $DB_TABLE SET password_sha256 = '$p_sha256' WHERE username = '$req_user'";
- $auth_logger->info("updated password for hrd style, user: ".$REQ_USER);
- return $DB_conn->do($UPD_QUERY);
- }
- ### end of subs
- ### begins setting up log
- #
- auth_log_init();
- $auth_logger->info("reason: auth begun , user: ", $REQ_USER);
- $server_name='web00.ortiche.net';
- #$auth_logger->debug("server ip: '$SERVER_IP'\n");
- #$auth_logger->debug("server name: '$server_name'\n");
- if ( $REQ_USER eq "" )
- {
- $auth_logger->fatal("reason: empty user");
- auth_return(-1, "");
- }
- $DB_conn = DBI->connect("DBI:mysql:database=$DB_DB;host=$DB_HOST", $DB_USER, $DB_PASS)
- || $auth_logger->fatal("fatal: connect() to DB")
- && auth_return(-1, "");
- ### runs query and checks
- #
- my $QUERY_pureftpd = $DB_conn->prepare("SELECT path, password_sha256, password_crypt FROM $DB_TABLE JOIN directories USING (dir_id) JOIN hosts_urls USING (url_id) JOIN hosts USING (host_id) WHERE username='$REQ_USER' AND ftp = 'Y' AND hostname = '$server_name'")
- || $auth_logger->fatal("prepare() query, reason: no user? no ftp=Y? wrong \$server_name ?, user: ".$REQ_USER)
- && auth_return(-1, "");
- $QUERY_pureftpd->execute()
- || $auth_logger->fatal("execute() query, reason: no user? no ftp=Y? wrong \$server_name ?, user: ".$REQ_USER)
- && auth_return(-1, "");
-
- my ( $READ_path, $READ_hrd_sha256, $READ_new_crypt ) = $QUERY_pureftpd->fetchrow_array();
- ### TODO: place here separated checks on ftp = 'Y' AND hostname = '$server_name' ; remove clause from above SELECT
- ###
- if ( $READ_path eq "" ) { $auth_logger->warn("warning: empty field PATH"); }
- if ( $READ_hrd_sha256 eq "" ) { $auth_logger->warn("warning: empty field HRD_SHA256"); }
- if ( $READ_new_crypt eq "" ) { $auth_logger->warn("warning: empty field NEW_CRYPT"); }
- if ( $QUERY_pureftpd->rows == 0 ) {
- $auth_logger->error("user not found, reason: no user? no ftp=Y ? wrong \$server_name ?, user: ".$REQ_USER);
- auth_return(0, "");
- } else {
- $auth_logger->info("User: ".$REQ_USER);
- if (check_crypt($REQ_PASS, $READ_new_crypt)) {
- $auth_logger->info("auth_ok:1, GOOD pwd match!, user: ".$REQ_USER);
- auth_return(1, $READ_path);
- } else {
- $auth_logger->info("auth_ok:-1, reason: wrong password from check_crypt(), user: ".$REQ_USER);
- auth_return(-1, "");
- }
- }
|