2015-10-23 10:01:45 +02:00
#!/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
#
2015-10-23 10:09:00 +02:00
$ SCRIPT_DIR = "/usr/local/ortiche/pureftpd-auth" ;
2015-10-23 10:01:45 +02:00
$ 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 ) ;
2018-10-04 16:35:18 +02:00
$ server_name = 'web01.indivia.net' ;
2015-10-23 10:01:45 +02:00
#$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 , "" ) ;
}
}