diff options
Diffstat (limited to 'LedgerSMB/Session/DB.pm')
-rwxr-xr-x | LedgerSMB/Session/DB.pm | 375 |
1 files changed, 206 insertions, 169 deletions
diff --git a/LedgerSMB/Session/DB.pm b/LedgerSMB/Session/DB.pm index d10932aa..df9b36ff 100755 --- a/LedgerSMB/Session/DB.pm +++ b/LedgerSMB/Session/DB.pm @@ -1,13 +1,13 @@ #===================================================================== -# LedgerSMB +# LedgerSMB # Small Medium Business Accounting software # http://www.ledgersmb.org/ -# +# # # Copyright (C) 2006 # This work contains copyrighted information from a number of sources all used -# with permission. It is released under the GNU General Public License -# Version 2 or, at your option, any later version. See COPYRIGHT file for +# with permission. It is released under the GNU General Public License +# Version 2 or, at your option, any later version. See COPYRIGHT file for # details. # # @@ -24,247 +24,284 @@ # # destroy - destroys session # -# password_check - compares the password with the stored cryted password +# password_check - compares the password with the stored cryted password # (ver. < 1.2) and the md5 one (ver. >= 1.2) #==================================================================== package Session; sub session_check { - use Time::HiRes qw(gettimeofday); + use Time::HiRes qw(gettimeofday); - my ($cookie, $form) = @_; - my ($sessionID, $transactionID, $token) = split /:/, $cookie; + my ( $cookie, $form ) = @_; + my ( $sessionID, $transactionID, $token ) = split /:/, $cookie; - # use the central database handle - my $dbh = ${LedgerSMB::Sysconfig::GLOBALDBH}; + # use the central database handle + my $dbh = ${LedgerSMB::Sysconfig::GLOBALDBH}; - my $checkQuery = $dbh->prepare("SELECT u.username, s.transaction_id + my $checkQuery = $dbh->prepare( + "SELECT u.username, s.transaction_id FROM session as s, users as u WHERE s.session_id = ? AND s.token = ? AND s.users_id = u.id - AND s.last_used > now() - ?::interval"); + AND s.last_used > now() - ?::interval" + ); - my $updateAge = $dbh->prepare("UPDATE session + my $updateAge = $dbh->prepare( + "UPDATE session SET last_used = now(), transaction_id = ? - WHERE session_id = ?;"); - - #must be an integer - $sessionID =~ s/[^0-9]//g; - $sessionID = int $sessionID; + WHERE session_id = ?;" + ); - $transactionID =~ s/[^0-9]//g; - $transactionID = int $transactionID; + #must be an integer + $sessionID =~ s/[^0-9]//g; + $sessionID = int $sessionID; - #must be 32 chars long and contain hex chars - $token =~ s/[^0-9a-f]//g; - $token = substr($token, 0, 32); + $transactionID =~ s/[^0-9]//g; + $transactionID = int $transactionID; - if (!$myconfig{timeout}){ - $timeout = "1 day"; - } else { - $timeout = "$myconfig{timeout} seconds"; - } + #must be 32 chars long and contain hex chars + $token =~ s/[^0-9a-f]//g; + $token = substr( $token, 0, 32 ); - $checkQuery->execute($sessionID, $token, $timeout) - || $form->dberror(__FILE__.':'.__LINE__.': Looking for session: '); - my $sessionValid = $checkQuery->rows; + if ( !$myconfig{timeout} ) { + $timeout = "1 day"; + } + else { + $timeout = "$myconfig{timeout} seconds"; + } - if($sessionValid){ + $checkQuery->execute( $sessionID, $token, $timeout ) + || $form->dberror( + __FILE__ . ':' . __LINE__ . ': Looking for session: ' ); + my $sessionValid = $checkQuery->rows; - #user has a valid session cookie, now check the user - my ($sessionLogin, $sessionTransaction) = $checkQuery->fetchrow_array; + if ($sessionValid) { - my $login = $form->{login}; - $login =~ s/[^a-zA-Z0-9._+@'-]//g; + #user has a valid session cookie, now check the user + my ( $sessionLogin, $sessionTransaction ) = $checkQuery->fetchrow_array; - if(($sessionLogin eq $login) and ($sessionTransaction eq $transactionID)){ + my $login = $form->{login}; + $login =~ s/[^a-zA-Z0-9._+@'-]//g; - #microseconds are more than random enough for transaction_id - my ($ignore, $newTransactionID) = gettimeofday(); + if ( ( $sessionLogin eq $login ) + and ( $sessionTransaction eq $transactionID ) ) + { - $newTransactionID = int $newTransactionID; - - $updateAge->execute($newTransactionID, $sessionID) - || $form->dberror(__FILE__.':'.__LINE__.': Updating session age: '); + #microseconds are more than random enough for transaction_id + my ( $ignore, $newTransactionID ) = gettimeofday(); - $newCookieValue = $sessionID . ':'.$newTransactionID.':' . $token; + $newTransactionID = int $newTransactionID; - #now update the cookie in the browser - print qq|Set-Cookie: LedgerSMB=$newCookieValue; path=/;\n|; - return 1; + $updateAge->execute( $newTransactionID, $sessionID ) + || $form->dberror( + __FILE__ . ':' . __LINE__ . ': Updating session age: ' ); - } else { - #something's wrong, they have the cookie, but wrong user or the wrong transaction id. Hijack attempt? - #destroy the session - my $sessionDestroy = $dbh->prepare(""); + $newCookieValue = + $sessionID . ':' . $newTransactionID . ':' . $token; - #delete the cookie in the browser - print qq|Set-Cookie: LedgerSMB=; path=/;\n|; - return 0; - } - - } else { - #cookie is not valid - #delete the cookie in the browser - print qq|Set-Cookie: LedgerSMB=; path=/;\n|; - return 0; - } -} + #now update the cookie in the browser + print qq|Set-Cookie: LedgerSMB=$newCookieValue; path=/;\n|; + return 1; -sub session_create { + } + else { - use Time::HiRes qw(gettimeofday); +#something's wrong, they have the cookie, but wrong user or the wrong transaction id. Hijack attempt? +#destroy the session + my $sessionDestroy = $dbh->prepare(""); - #microseconds are more than random enough for transaction_id - my ($ignore, $newTransactionID) = gettimeofday(); - $newTransactionID = int $newTransactionID; + #delete the cookie in the browser + print qq|Set-Cookie: LedgerSMB=; path=/;\n|; + return 0; + } - my ($form) = @_; + } + else { - if (! $ENV{HTTP_HOST}){ - #don't create cookies or sessions for CLI use - return 1; - } - - # use the central database handle - my $dbh = ${LedgerSMB::Sysconfig::GLOBALDBH}; - - # TODO Change this to use %myconfig - my $deleteExisting = $dbh->prepare( - "DELETE - FROM session - WHERE session.users_id = (select id from users where username = ?) - AND age(last_used) > ?::interval"); - - my $seedRandom = $dbh->prepare("SELECT setseed(?);"); + #cookie is not valid + #delete the cookie in the browser + print qq|Set-Cookie: LedgerSMB=; path=/;\n|; + return 0; + } +} - my $fetchSequence = $dbh->prepare("SELECT nextval('session_session_id_seq'), md5(random());"); - - my $createNew = $dbh->prepare("INSERT INTO session (session_id, users_id, token, transaction_id) - VALUES(?, (SELECT id - FROM users - WHERE username = ?), ?, ?);"); +sub session_create { + use Time::HiRes qw(gettimeofday); - # this is assuming that $form->{login} is safe, which might be a bad assumption - # so, I'm going to remove some chars, which might make previously valid logins invalid - my $login = $form->{login}; - $login =~ s/[^a-zA-Z0-9._+@'-]//g; + #microseconds are more than random enough for transaction_id + my ( $ignore, $newTransactionID ) = gettimeofday(); + $newTransactionID = int $newTransactionID; - #delete any existing stale sessions with this login if they exist - if (!$myconfig{timeout}){ - $myconfig{timeout} = 86400; - } + my ($form) = @_; - $deleteExisting->execute($login, "$myconfig{timeout} seconds") - || $form->dberror(__FILE__.':'.__LINE__.': Delete from session: '); + if ( !$ENV{HTTP_HOST} ) { - #doing the random stuff in the db so that LedgerSMB won't - #require a good random generator - maybe this should be reviewed, pgsql's isn't great either - $fetchSequence->execute() || $form->dberror(__FILE__.':'.__LINE__.': Fetch sequence id: '); - my ($newSessionID, $newToken) = $fetchSequence->fetchrow_array; + #don't create cookies or sessions for CLI use + return 1; + } - #create a new session - $createNew->execute($newSessionID, $login, $newToken, $newTransactionID) - || $form->dberror(__FILE__.':'.__LINE__.': Create new session: '); + # use the central database handle + my $dbh = ${LedgerSMB::Sysconfig::GLOBALDBH}; - #reseed the random number generator - my $randomSeed = 1.0 * ('0.'. (time() ^ ($$ + ($$ <<15)))); + # TODO Change this to use %myconfig + my $deleteExisting = $dbh->prepare( + "DELETE + FROM session + WHERE session.users_id = (select id from users where username = ?) + AND age(last_used) > ?::interval" + ); - $seedRandom->execute($randomSeed) - || $form->dberror(__FILE__.':'.__LINE__.': Reseed random generator: '); + my $seedRandom = $dbh->prepare("SELECT setseed(?);"); - $newCookieValue = $newSessionID . ':'.$newTransactionID.':' . $newToken; + my $fetchSequence = + $dbh->prepare("SELECT nextval('session_session_id_seq'), md5(random());"); - #now set the cookie in the browser - #TODO set domain from ENV, also set path to install path - print qq|Set-Cookie: LedgerSMB=$newCookieValue; path=/;\n|; - $form->{LedgerSMB} = $newCookieValue; + my $createNew = $dbh->prepare( + "INSERT INTO session (session_id, users_id, token, transaction_id) + VALUES(?, (SELECT id + FROM users + WHERE username = ?), ?, ?);" + ); + +# this is assuming that $form->{login} is safe, which might be a bad assumption +# so, I'm going to remove some chars, which might make previously valid logins invalid + my $login = $form->{login}; + $login =~ s/[^a-zA-Z0-9._+@'-]//g; + + #delete any existing stale sessions with this login if they exist + if ( !$myconfig{timeout} ) { + $myconfig{timeout} = 86400; + } + + $deleteExisting->execute( $login, "$myconfig{timeout} seconds" ) + || $form->dberror( + __FILE__ . ':' . __LINE__ . ': Delete from session: ' ); + +#doing the random stuff in the db so that LedgerSMB won't +#require a good random generator - maybe this should be reviewed, pgsql's isn't great either + $fetchSequence->execute() + || $form->dberror( __FILE__ . ':' . __LINE__ . ': Fetch sequence id: ' ); + my ( $newSessionID, $newToken ) = $fetchSequence->fetchrow_array; + + #create a new session + $createNew->execute( $newSessionID, $login, $newToken, $newTransactionID ) + || $form->dberror( __FILE__ . ':' . __LINE__ . ': Create new session: ' ); + + #reseed the random number generator + my $randomSeed = 1.0 * ( '0.' . ( time() ^ ( $$ + ( $$ << 15 ) ) ) ); + + $seedRandom->execute($randomSeed) + || $form->dberror( + __FILE__ . ':' . __LINE__ . ': Reseed random generator: ' ); + + $newCookieValue = $newSessionID . ':' . $newTransactionID . ':' . $newToken; + + #now set the cookie in the browser + #TODO set domain from ENV, also set path to install path + print qq|Set-Cookie: LedgerSMB=$newCookieValue; path=/;\n|; + $form->{LedgerSMB} = $newCookieValue; } sub session_destroy { - my ($form) = @_; + my ($form) = @_; - my $login = $form->{login}; - $login =~ s/[^a-zA-Z0-9._+@'-]//g; + my $login = $form->{login}; + $login =~ s/[^a-zA-Z0-9._+@'-]//g; - # use the central database handle - my $dbh = ${LedgerSMB::Sysconfig::GLOBALDBH}; + # use the central database handle + my $dbh = ${LedgerSMB::Sysconfig::GLOBALDBH}; - my $deleteExisting = $dbh->prepare(" + my $deleteExisting = $dbh->prepare( " DELETE FROM session WHERE users_id = (select id from users where username = ?) - "); + " ); - $deleteExisting->execute($login) - || $form->dberror(__FILE__.':'.__LINE__.': Delete from session: '); + $deleteExisting->execute($login) + || $form->dberror( + __FILE__ . ':' . __LINE__ . ': Delete from session: ' ); - #delete the cookie in the browser - print qq|Set-Cookie: LedgerSMB=; path=/;\n|; + #delete the cookie in the browser + print qq|Set-Cookie: LedgerSMB=; path=/;\n|; } sub password_check { - use Digest::MD5; + use Digest::MD5; - my ($form, $username, $password) = @_; + my ( $form, $username, $password ) = @_; - $username =~ s/[^a-zA-Z0-9._+@'-]//g; + $username =~ s/[^a-zA-Z0-9._+@'-]//g; - # use the central database handle - my $dbh = ${LedgerSMB::Sysconfig::GLOBALDBH}; + # use the central database handle + my $dbh = ${LedgerSMB::Sysconfig::GLOBALDBH}; - my $fetchPassword = $dbh->prepare("SELECT u.username, uc.password, uc.crypted_password + my $fetchPassword = $dbh->prepare( + "SELECT u.username, uc.password, uc.crypted_password FROM users as u, users_conf as uc WHERE u.username = ? - AND u.id = uc.id;"); + AND u.id = uc.id;" + ); - $fetchPassword->execute($username) || $form->dberror(__FILE__.':'.__LINE__.': Fetching password : '); + $fetchPassword->execute($username) + || $form->dberror( __FILE__ . ':' . __LINE__ . ': Fetching password : ' ); - my ($dbusername, $md5Password, $cryptPassword) = $fetchPassword->fetchrow_array; - if ($dbusername ne $username) { - # User data retrieved from db not for the requested user - return 0; - } elsif ($cryptPassword){ - #First time login from old system, check crypted password + my ( $dbusername, $md5Password, $cryptPassword ) = + $fetchPassword->fetchrow_array; + if ( $dbusername ne $username ) { - if ((crypt $password, substr($username, 0, 2)) eq $cryptPassword) { + # User data retrieved from db not for the requested user + return 0; + } + elsif ($cryptPassword) { - #password was good, convert to md5 password and null crypted - my $updatePassword = $dbh->prepare("UPDATE users_conf + #First time login from old system, check crypted password + + if ( ( crypt $password, substr( $username, 0, 2 ) ) eq $cryptPassword ) + { + + #password was good, convert to md5 password and null crypted + my $updatePassword = $dbh->prepare( + "UPDATE users_conf SET password = md5(?), crypted_password = null FROM users WHERE users_conf.id = users.id - AND users.username = ?;"); - - $updatePassword->execute($password, $username) - || $form->dberror(__FILE__.':'.__LINE__.': Converting password : '); - - return 1; - - } else { - return 0; #password failed - } - - } elsif ($md5Password){ - - if ($md5Password ne (Digest::MD5::md5_hex $password) ) { - return 0; - } - else { - return 1; - } - - } else { - #both the md5Password and cryptPasswords were blank - return 0; - } + AND users.username = ?;" + ); + + $updatePassword->execute( $password, $username ) + || $form->dberror( + __FILE__ . ':' . __LINE__ . ': Converting password : ' ); + + return 1; + + } + else { + return 0; #password failed + } + + } + elsif ($md5Password) { + + if ( $md5Password ne ( Digest::MD5::md5_hex $password) ) { + return 0; + } + else { + return 1; + } + + } + else { + + #both the md5Password and cryptPasswords were blank + return 0; + } } 1; |