diff options
author | Yorhel <git@yorhel.nl> | 2009-07-30 09:43:37 +0200 |
---|---|---|
committer | Yorhel <git@yorhel.nl> | 2009-07-30 09:45:00 +0200 |
commit | 4e1a84ae9ee6dbc5ea18517a9112ae1821797eb7 (patch) | |
tree | 2a13682473e2e7dc9a1bb59fe0e32f1f294b77dd /lib | |
parent | d6a9d1c5d613c13cf9de4ecf254ba864341975be (diff) | |
parent | a04360aa66453809395a09a66ccd63e46a4cf519 (diff) |
Merge branch 'auth' of git://3decibels.net/vndb into beta
Conflicts:
util/dump.sql
util/updates/update_2.6.sql
Also updated ChangeLog and made some tiny style changes.
Diffstat (limited to 'lib')
-rw-r--r-- | lib/VNDB/DB/Users.pm | 50 | ||||
-rw-r--r-- | lib/VNDB/Handler/Users.pm | 11 | ||||
-rw-r--r-- | lib/VNDB/Util/Auth.pm | 86 |
3 files changed, 115 insertions, 32 deletions
diff --git a/lib/VNDB/DB/Users.pm b/lib/VNDB/DB/Users.pm index d5cdcc6c..eaed1234 100644 --- a/lib/VNDB/DB/Users.pm +++ b/lib/VNDB/DB/Users.pm @@ -5,7 +5,7 @@ use strict; use warnings; use Exporter 'import'; -our @EXPORT = qw|dbUserGet dbUserEdit dbUserAdd dbUserDel|; +our @EXPORT = qw|dbUserGet dbUserEdit dbUserAdd dbUserDel dbSessionAdd dbSessionDel dbSessionCheck|; # %options->{ username passwd mail order uid ip registered search results page what } @@ -75,7 +75,7 @@ sub dbUserEdit { my %h; defined $o{$_} && ($h{$_.' = ?'} = $o{$_}) - for (qw| username mail rank show_nsfw show_list skin customcss |); + for (qw| username mail rank show_nsfw show_list skin customcss salt |); $h{'passwd = decode(?, \'hex\')'} = $o{passwd} if defined $o{passwd}; @@ -88,11 +88,11 @@ sub dbUserEdit { } -# username, md5(pass), mail, [ip] +# username, pass(ecrypted), salt, mail, [ip] sub dbUserAdd { my($s, @o) = @_; - $s->dbExec(q|INSERT INTO users (username, passwd, mail, ip, registered) VALUES(?, decode(?, 'hex'), ?, ?, ?)|, - @o[0..2], $o[3]||$s->reqIP, time); + $s->dbExec(q|INSERT INTO users (username, passwd, salt, mail, ip, registered) VALUES(?, decode(?, 'hex'), ?, ?, ?, ?)|, + @o[0..3], $o[4]||$s->reqIP, time); } @@ -104,6 +104,7 @@ sub dbUserDel { q|DELETE FROM rlists WHERE uid = ?|, q|DELETE FROM wlists WHERE uid = ?|, q|DELETE FROM votes WHERE uid = ?|, + q|DELETE FROM sessions WHERE uid = ?|, q|UPDATE changes SET requester = 0 WHERE requester = ?|, q|UPDATE threads_posts SET uid = 0 WHERE uid = ?|, q|DELETE FROM users WHERE id = ?| @@ -111,4 +112,43 @@ sub dbUserDel { } +# Adds a session to the database +# If no expiration is supplied the database default is used +# uid, 40 character session token, expiration time (timestamp) +sub dbSessionAdd { + my($s, @o) = @_; + if (defined $o[2]) { + $s->dbExec(q|INSERT INTO sessions (uid, token, expiration) VALUES(?, ?, ?)|, + @o); + } else { + $s->dbExec(q|INSERT INTO sessions (uid, token) VALUES(?, ?)|, + @o); + } +} + + +# Deletes session(s) from the database +# If no token is supplied, all sessions for the uid are destroyed +# uid, token (optional) +sub dbSessionDel { + my($s, @o) = @_; + if (defined $o[1]) { + $s->dbExec(q|DELETE FROM sessions WHERE uid = ? AND token = ?|, + @o[0..1]); + } else { + $s->dbExec(q|DELETE FROM sessions WHERE uid = ?|, + $o[0]); + } +} + + +# Queries the database for the validity of a session +# Returns 1 if corresponding session found, 0 if not +# uid, token +sub dbSessionCheck { + my($s, @o) = @_; + return $s->dbRow(q|SELECT count(uid) AS count FROM sessions WHERE uid = ? AND token = ? LIMIT 1|, @o)->{count}||0; +} + + 1; diff --git a/lib/VNDB/Handler/Users.pm b/lib/VNDB/Handler/Users.pm index d4755ae1..982d6089 100644 --- a/lib/VNDB/Handler/Users.pm +++ b/lib/VNDB/Handler/Users.pm @@ -4,7 +4,6 @@ package VNDB::Handler::Users; use strict; use warnings; use YAWF ':html'; -use Digest::MD5 'md5_hex'; use VNDB::Func; @@ -183,7 +182,9 @@ sub newpass { if(!$frm->{_err}) { my @chars = ( 'A'..'Z', 'a'..'z', 0..9 ); my $pass = join '', map $chars[int rand $#chars+1], 0..8; - $self->dbUserEdit($u->{id}, passwd => md5_hex($pass)); + my %o; + ($o{passwd}, $o{salt}) = $self->authPreparePass($pass); + $self->dbUserEdit($u->{id}, %o); my $body = <<'__'; Hello %s, @@ -258,7 +259,8 @@ sub register { push @{$frm->{_err}}, 'oneaday' if !$frm->{_err} && $self->dbUserGet(ip => $self->reqIP, registered => time-24*3600)->[0]{id}; if(!$frm->{_err}) { - $self->dbUserAdd($frm->{usrname}, md5_hex($frm->{usrpass}), $frm->{mail}); + my ($pass, $salt) = $self->authPreparePass($frm->{usrpass}); + $self->dbUserAdd($frm->{usrname}, $pass, $salt, $frm->{mail}); return $self->authLogin($frm->{usrname}, $frm->{usrpass}, '/'); } } @@ -330,10 +332,11 @@ sub edit { $o{mail} = $frm->{mail}; $o{skin} = $frm->{skin}; $o{customcss} = $frm->{customcss}; - $o{passwd} = md5_hex($frm->{usrpass}) if $frm->{usrpass}; + ($o{passwd}, $o{salt}) = $self->authPreparePass($frm->{usrpass}) if $frm->{usrpass}; $o{show_list} = $frm->{flags_list} ? 1 : 0; $o{show_nsfw} = $frm->{flags_nsfw} ? 1 : 0; $self->dbUserEdit($uid, %o); + $self->dbSessionDel($uid) if $frm->{usrpass}; return $self->resRedirect("/u$uid/edit?d=1", 'post') if $uid != $self->authInfo->{id} || !$frm->{usrpass}; return $self->authLogin($frm->{usrname}||$u->{username}, $frm->{usrpass}, "/u$uid/edit?d=1"); } diff --git a/lib/VNDB/Util/Auth.pm b/lib/VNDB/Util/Auth.pm index cb0751e9..00700e6e 100644 --- a/lib/VNDB/Util/Auth.pm +++ b/lib/VNDB/Util/Auth.pm @@ -1,19 +1,17 @@ package VNDB::Util::Auth; -# This module is just a small improvement of the 1.x equivalent -# and is designed to work with the cookies and database of VNDB 1.x -# without modifications. A proper and more secure (incompatible) -# implementation should be written at some point. use strict; use warnings; use Exporter 'import'; -use Digest::MD5 'md5_hex'; -use Crypt::Lite; +use Digest::MD5 'md5'; +use Digest::SHA qw|sha1_hex sha256 sha256_hex|; +use Time::HiRes; +use POSIX 'strftime'; -our @EXPORT = qw| authInit authLogin authLogout authInfo authCan |; +our @EXPORT = qw| authInit authLogin authLogout authInfo authCan authPreparePass |; # initializes authentication information and checks the vndb_auth cookie @@ -22,12 +20,10 @@ sub authInit { $self->{_auth} = undef; my $cookie = $self->reqCookie('vndb_auth'); - return 0 if !$cookie; - my $str = Crypt::Lite->new()->decrypt($cookie, md5_hex($self->{cookie_key})); - return 0 if length($str) < 36; - my $pass = substr($str, 4, 32); - my $user = substr($str, 36); - _authCheck($self, $user, $pass); + return 0 if !$cookie || length($cookie) < 41; + my $token = substr($cookie, 0, 40); + my $uid = substr($cookie, 40); + $self->{_auth} = $self->dbUserGet(uid => $uid, what => 'mymessages')->[0] if $self->dbSessionCheck($uid, $token); } @@ -36,15 +32,21 @@ sub authInit { sub authLogin { my $self = shift; my $user = lc(scalar shift); - my $pass = md5_hex(shift); + my $pass = shift; my $to = shift; if(_authCheck($self, $user, $pass)) { - (my $cookie = Crypt::Lite->new()->encrypt("VNDB$pass$user", md5_hex($self->{cookie_key}))) =~ s/\r?\n//g; + my $token = sha1_hex(join('', Time::HiRes::gettimeofday()) . join('', map chr(rand(93)+33), 1..9)); + my $expiration = time + 31536000; # 1yr + my $cookie = $token . $self->{_auth}{id}; + $self->dbSessionAdd($self->{_auth}{id}, $token); + + my $expstr = strftime("%a, %d %b %Y %H:%M:%S GMT", gmtime($expiration)); $self->resRedirect($to, 'post'); - $self->resHeader('Set-Cookie', "vndb_auth=$cookie; expires=Sat, 01-Jan-2030 00:00:00 GMT; path=/; domain=$self->{cookie_domain}"); + $self->resHeader('Set-Cookie', "vndb_auth=$cookie; expires=$expstr; path=/; domain=$self->{cookie_domain}"); return 1; } + return 0; } @@ -52,6 +54,14 @@ sub authLogin { # clears authentication cookie and redirects to / sub authLogout { my $self = shift; + + my $cookie = $self->reqCookie('vndb_auth'); + if ($cookie && length($cookie) >= 41) { + my $token = substr($cookie, 0, 40); + my $uid = substr($cookie, 40); + $self->dbSessionDel($uid, $token); + } + $self->resRedirect('/', 'temp'); $self->resHeader('Set-Cookie', "vndb_auth= ; expires=Sat, 01-Jan-2000 00:00:00 GMT; path=/; domain=$self->{cookie_domain}"); } @@ -75,20 +85,50 @@ sub authCan { # Checks for a valid login and writes information in _auth -# Arguments: user, md5_hex(pass) +# Arguments: user, pass # Returns: 1 if login is valid, 0 otherwise sub _authCheck { my($self, $user, $pass) = @_; - return 0 if - !$user || length($user) > 15 || length($user) < 2 - || !$pass || length($pass) != 32; + return 0 if !$user || length($user) > 15 || length($user) < 2 || !$pass; - my $d = $self->dbUserGet(username => $user, passwd => $pass, what => 'mymessages')->[0]; + my $d = $self->dbUserGet(username => $user, what => 'mymessages')->[0]; return 0 if !defined $d->{id} || !$d->{rank}; - $self->{_auth} = $d; - return 1; + if (_authEncryptPass($self, $pass, $d->{salt}, 1) eq $d->{passwd}) { + $self->{_auth} = $d; + return 1; + } + if (md5($pass) eq $d->{passwd}) { + $self->{_auth} = $d; + my %o; + ($o{passwd}, $o{salt}) = authPreparePass($self, $pass); + $self->dbUserEdit($d->{id}, %o); + return 1; + } + + return 0; +} + + +# Encryption algorithm for user passwords +# Arguments: self, pass, salt, binary mode +# Returns: encrypted password +sub _authEncryptPass{ + my ($self, $pass, $salt, $bin) = @_; + return sha256($self->{global_salt} . $pass . $salt) if $bin; + return sha256_hex($self->{global_salt} . $pass . $salt); +} + + +# Prepares a plaintext password for database storage +# Arguments: pass +# Returns: list (pass, salt) +sub authPreparePass{ + my($self, $pass) = @_; + my $salt = join '', map chr(rand(93)+33), 1..9; + my $hash = _authEncryptPass($self, $pass, $salt); + return ($hash, $salt); } |