summaryrefslogtreecommitdiff
path: root/LedgerSMB/Session/DB.pm
blob: ecd878145e2444e459769a3dd6f7b1436269003f (plain)
  1. #=====================================================================
  2. # LedgerSMB
  3. # Small Medium Business Accounting software
  4. # http://www.ledgersmb.org/
  5. #
  6. #
  7. # Copyright (C) 2006
  8. # This work contains copyrighted information from a number of sources all used
  9. # with permission. It is released under the GNU General Public License
  10. # Version 2 or, at your option, any later version. See COPYRIGHT file for
  11. # details.
  12. #
  13. #
  14. #======================================================================
  15. #
  16. # This file has undergone whitespace cleanup.
  17. #
  18. #======================================================================
  19. # This package contains session related functions:
  20. #
  21. # check - checks validity of session based on the user's cookie and login
  22. #
  23. # create - creates a new session, writes cookie upon success
  24. #
  25. # destroy - destroys session
  26. #
  27. # password_check - compares the password with the stored cryted password
  28. # (ver. < 1.2) and the md5 one (ver. >= 1.2)
  29. #====================================================================
  30. package Session;
  31. sub session_check {
  32. use Time::HiRes qw(gettimeofday);
  33. my ($cookie, $form) = @_;
  34. my ($sessionID, $transactionID, $token) = split /:/, $cookie;
  35. # use the central database handle
  36. my $dbh = ${LedgerSMB::Sysconfig::GLOBALDBH};
  37. my $checkQuery = $dbh->prepare("SELECT u.username, s.transaction_id
  38. FROM session as s, users as u
  39. WHERE s.session_id = ?
  40. AND s.token = ?
  41. AND s.users_id = u.id
  42. AND s.last_used > now() - ?::interval");
  43. my $updateAge = $dbh->prepare("UPDATE session
  44. SET last_used = now(),
  45. transaction_id = ?
  46. WHERE session_id = ?;");
  47. #must be an integer
  48. $sessionID =~ s/[^0-9]//g;
  49. $sessionID = int $sessionID;
  50. $transactionID =~ s/[^0-9]//g;
  51. $transactionID = int $transactionID;
  52. #must be 32 chars long and contain hex chars
  53. $token =~ s/[^0-9a-f]//g;
  54. $token = substr($token, 0, 32);
  55. if (!$myconfig{timeout}){
  56. $timeout = "1 day";
  57. } else {
  58. $timeout = "$myconfig{timeout} seconds";
  59. }
  60. $checkQuery->execute($sessionID, $token, $timeout)
  61. || $form->dberror(__FILE__.':'.__LINE__.': Looking for session: ');
  62. my $sessionValid = $checkQuery->rows;
  63. if($sessionValid){
  64. #user has a valid session cookie, now check the user
  65. my ($sessionLogin, $sessionTransaction) = $checkQuery->fetchrow_array;
  66. my $login = $form->{login};
  67. $login =~ s/[^a-zA-Z0-9._+@'-]//g;
  68. if(($sessionLogin eq $login) and ($sessionTransaction eq $transactionID)){
  69. #microseconds are more than random enough for transaction_id
  70. my ($ignore, $newTransactionID) = gettimeofday();
  71. $newTransactionID = int $newTransactionID;
  72. $updateAge->execute($newTransactionID, $sessionID)
  73. || $form->dberror(__FILE__.':'.__LINE__.': Updating session age: ');
  74. $newCookieValue = $sessionID . ':'.$newTransactionID.':' . $token;
  75. #now update the cookie in the browser
  76. print qq|Set-Cookie: LedgerSMB=$newCookieValue; path=/;\n|;
  77. return 1;
  78. } else {
  79. #something's wrong, they have the cookie, but wrong user or the wrong transaction id. Hijack attempt?
  80. #destroy the session
  81. my $sessionDestroy = $dbh->prepare("");
  82. #delete the cookie in the browser
  83. print qq|Set-Cookie: LedgerSMB=; path=/;\n|;
  84. return 0;
  85. }
  86. } else {
  87. #cookie is not valid
  88. #delete the cookie in the browser
  89. print qq|Set-Cookie: LedgerSMB=; path=/;\n|;
  90. return 0;
  91. }
  92. }
  93. sub session_create {
  94. use Time::HiRes qw(gettimeofday);
  95. #microseconds are more than random enough for transaction_id
  96. my ($ignore, $newTransactionID) = gettimeofday();
  97. $newTransactionID = int $newTransactionID;
  98. my ($form) = @_;
  99. if (! $ENV{HTTP_HOST}){
  100. #don't create cookies or sessions for CLI use
  101. return 1;
  102. }
  103. # use the central database handle
  104. my $dbh = ${LedgerSMB::Sysconfig::GLOBALDBH};
  105. # TODO Change this to use %myconfig
  106. my $deleteExisting = $dbh->prepare("DELETE FROM session
  107. USING users
  108. WHERE users.username = ?
  109. AND users.id = session.users_id
  110. AND age(last_used) > ?::interval");
  111. my $seedRandom = $dbh->prepare("SELECT setseed(?);");
  112. my $fetchSequence = $dbh->prepare("SELECT nextval('session_session_id_seq'), md5(random());");
  113. my $createNew = $dbh->prepare("INSERT INTO session (session_id, users_id, token, transaction_id)
  114. VALUES(?, (SELECT id
  115. FROM users
  116. WHERE username = ?), ?, ?);");
  117. # this is assuming that $form->{login} is safe, which might be a bad assumption
  118. # so, I'm going to remove some chars, which might make previously valid logins invalid
  119. my $login = $form->{login};
  120. $login =~ s/[^a-zA-Z0-9._+@'-]//g;
  121. #delete any existing stale sessions with this login if they exist
  122. if (!$myconfig{timeout}){
  123. $myconfig{timeout} = 86400;
  124. }
  125. $deleteExisting->execute($login, "$myconfig{timeout} seconds")
  126. || $form->dberror(__FILE__.':'.__LINE__.': Delete from session: ');
  127. #doing the random stuff in the db so that LedgerSMB won't
  128. #require a good random generator - maybe this should be reviewed, pgsql's isn't great either
  129. $fetchSequence->execute() || $form->dberror(__FILE__.':'.__LINE__.': Fetch sequence id: ');
  130. my ($newSessionID, $newToken) = $fetchSequence->fetchrow_array;
  131. #create a new session
  132. $createNew->execute($newSessionID, $login, $newToken, $newTransactionID)
  133. || $form->dberror(__FILE__.':'.__LINE__.': Create new session: ');
  134. #reseed the random number generator
  135. my $randomSeed = 1.0 * ('0.'. (time() ^ ($$ + ($$ <<15))));
  136. $seedRandom->execute($randomSeed)
  137. || $form->dberror(__FILE__.':'.__LINE__.': Reseed random generator: ');
  138. $newCookieValue = $newSessionID . ':'.$newTransactionID.':' . $newToken;
  139. #now set the cookie in the browser
  140. #TODO set domain from ENV, also set path to install path
  141. print qq|Set-Cookie: LedgerSMB=$newCookieValue; path=/;\n|;
  142. $form->{LedgerSMB} = $newCookieValue;
  143. }
  144. sub session_destroy {
  145. my ($form) = @_;
  146. my $login = $form->{login};
  147. $login =~ s/[^a-zA-Z0-9._+@'-]//g;
  148. # use the central database handle
  149. my $dbh = ${LedgerSMB::Sysconfig::GLOBALDBH};
  150. my $deleteExisting = $dbh->prepare("DELETE FROM session
  151. USING users
  152. WHERE users.username = ?
  153. AND users.id = session.users_id;");
  154. $deleteExisting->execute($login)
  155. || $form->dberror(__FILE__.':'.__LINE__.': Delete from session: ');
  156. #delete the cookie in the browser
  157. print qq|Set-Cookie: LedgerSMB=; path=/;\n|;
  158. }
  159. sub password_check {
  160. use Digest::MD5;
  161. my ($form, $username, $password) = @_;
  162. $username =~ s/[^a-zA-Z0-9._+@'-]//g;
  163. # use the central database handle
  164. my $dbh = ${LedgerSMB::Sysconfig::GLOBALDBH};
  165. my $fetchPassword = $dbh->prepare("SELECT u.username, uc.password, uc.crypted_password
  166. FROM users as u, users_conf as uc
  167. WHERE u.username = ?
  168. AND u.id = uc.id;");
  169. $fetchPassword->execute($username) || $form->dberror(__FILE__.':'.__LINE__.': Fetching password : ');
  170. my ($dbusername, $md5Password, $cryptPassword) = $fetchPassword->fetchrow_array;
  171. if ($dbusername ne $username) {
  172. # User data retrieved from db not for the requested user
  173. return 0;
  174. } elsif ($cryptPassword){
  175. #First time login from old system, check crypted password
  176. if ((crypt $password, substr($username, 0, 2)) eq $cryptPassword) {
  177. #password was good, convert to md5 password and null crypted
  178. my $updatePassword = $dbh->prepare("UPDATE users_conf
  179. SET password = md5(?),
  180. crypted_password = null
  181. FROM users
  182. WHERE users_conf.id = users.id
  183. AND users.username = ?;");
  184. $updatePassword->execute($password, $username)
  185. || $form->dberror(__FILE__.':'.__LINE__.': Converting password : ');
  186. return 1;
  187. } else {
  188. return 0; #password failed
  189. }
  190. } elsif ($md5Password){
  191. if ($md5Password ne (Digest::MD5::md5_hex $password) ) {
  192. return 0;
  193. }
  194. else {
  195. return 1;
  196. }
  197. } else {
  198. #both the md5Password and cryptPasswords were blank
  199. return 0;
  200. }
  201. }
  202. 1;