summaryrefslogtreecommitdiff
path: root/LedgerSMB/Auth/DB.pm
blob: 4ffa5a0a40774fe58af69e0710d48ec0ecc29963 (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 LedgerSMB::Auth;
  31. use MIME::Base64;
  32. use LedgerSMB::Sysconfig;
  33. use strict;
  34. sub session_check {
  35. use Time::HiRes qw(gettimeofday);
  36. my ( $cookie, $form ) = @_;
  37. my $path = ($ENV{SCRIPT_NAME});
  38. $path =~ s|[^/]*$||;
  39. if ($cookie eq 'Login'){
  40. return session_create($form);
  41. }
  42. my $timeout;
  43. my $dbh = $form->{dbh};
  44. my $checkQuery = $dbh->prepare(
  45. "SELECT * FROM session_check(?, ?)");
  46. my ($sessionID, $token, $company) = split(/:/, $cookie);
  47. $form->{company} ||= $company;
  48. #must be an integer
  49. $sessionID =~ s/[^0-9]//g;
  50. $sessionID = int $sessionID;
  51. if ( !$form->{timeout} ) {
  52. $timeout = "1 day";
  53. }
  54. else {
  55. $timeout = "$form->{timeout} seconds";
  56. }
  57. $checkQuery->execute( $sessionID, $token)
  58. || $form->dberror(
  59. __FILE__ . ':' . __LINE__ . ': Looking for session: ' );
  60. my $sessionValid = $checkQuery->rows;
  61. $dbh->commit;
  62. if ($sessionValid) {
  63. #user has a valid session cookie, now check the user
  64. my ( $session_ref) = $checkQuery->fetchrow_hashref('NAME_lc');
  65. my $login = $form->{login};
  66. $login =~ s/[^a-zA-Z0-9._+\@'-]//g;
  67. if (( $session_ref ))
  68. {
  69. my $newCookieValue =
  70. $session_ref->{session_id} . ':' . $session_ref->{token} . ':' . $form->{company};
  71. #now update the cookie in the browser
  72. print qq|Set-Cookie: ${LedgerSMB::Sysconfig::cookie_name}=$newCookieValue; path=$path;\n|;
  73. return 1;
  74. }
  75. else {
  76. #something's wrong, they have the cookie, but wrong user or the wrong transaction id. Hijack attempt?
  77. #destroy the session
  78. my $sessionDestroy = $dbh->prepare("");
  79. #delete the cookie in the browser
  80. print qq|Set-Cookie: ${LedgerSMB::Sysconfig::cookie_name}=; path=$path;\n|;
  81. return 0;
  82. }
  83. }
  84. else {
  85. #cookie is not valid
  86. #delete the cookie in the browser
  87. print qq|Set-Cookie: ${LedgerSMB::Sysconfig::cookie_name}=; path=$path;\n|;
  88. return 0;
  89. }
  90. }
  91. sub session_create {
  92. my ($lsmb) = @_;
  93. my $path = ($ENV{SCRIPT_NAME});
  94. $path =~ s|[^/]*$||;
  95. use Time::HiRes qw(gettimeofday);
  96. my $dbh = $lsmb->{dbh};
  97. my $login = $lsmb->{login};
  98. #microseconds are more than random enough for transaction_id
  99. my ( $ignore, $newTransactionID ) = gettimeofday();
  100. $newTransactionID = int $newTransactionID;
  101. if ( !$ENV{GATEWAY_INTERFACE} ) {
  102. #don't create cookies or sessions for CLI use
  103. return 1;
  104. }
  105. # TODO Change this to use %myconfig
  106. my $deleteExisting = $dbh->prepare(
  107. "DELETE
  108. FROM session
  109. WHERE session.users_id = (select id from users where username = ?)"
  110. );
  111. my $seedRandom = $dbh->prepare("SELECT setseed(?);");
  112. my $fetchSequence =
  113. $dbh->prepare("SELECT nextval('session_session_id_seq'), md5(random()::text);");
  114. my $createNew = $dbh->prepare(
  115. "INSERT INTO session (session_id, users_id, token, transaction_id)
  116. VALUES(?, (SELECT id
  117. FROM users
  118. WHERE username = SESSION_USER), ?, ?);"
  119. );
  120. # this is assuming that the login is safe, which might be a bad assumption
  121. # so, I'm going to remove some chars, which might make previously valid
  122. # logins invalid --CM
  123. # I am changing this to use HTTP Basic Auth credentials for now. -- CT
  124. my $auth = $ENV{HTTP_AUTHORIZATION};
  125. $auth =~ s/^Basic //i;
  126. #delete any existing stale sessions with this login if they exist
  127. if ( !$lsmb->{timeout} ) {
  128. $lsmb->{timeout} = 86400;
  129. }
  130. $deleteExisting->execute( $login)
  131. || $lsmb->dberror(
  132. __FILE__ . ':' . __LINE__ . ': Delete from session: ' . $DBI::errstr);
  133. #doing the random stuff in the db so that LedgerSMB won't
  134. #require a good random generator - maybe this should be reviewed,
  135. #pgsql's isn't great either -CM
  136. #
  137. #I think we should be OK. The random number generator is only a small part
  138. #of the credentials in 1.3.x, and for people that need greater security, there
  139. #is always Kerberos.... -- CT
  140. $fetchSequence->execute()
  141. || $lsmb->dberror( __FILE__ . ':' . __LINE__ . ': Fetch sequence id: ' );
  142. my ( $newSessionID, $newToken ) = $fetchSequence->fetchrow_array;
  143. #create a new session
  144. $createNew->execute( $newSessionID, $newToken, $newTransactionID )
  145. || $lsmb->dberror( __FILE__ . ':' . __LINE__ . ": Create new session: \n".
  146. $lsmb->{dbh}->errstr() );
  147. #reseed the random number generator
  148. my $randomSeed = 1.0 * ( '0.' . ( time() ^ ( $$ + ( $$ << 15 ) ) ) );
  149. $seedRandom->execute($randomSeed)
  150. || $lsmb->dberror(
  151. __FILE__ . ':' . __LINE__ . ': Reseed random generator: ' );
  152. my $newCookieValue = $newSessionID . ':' . $newToken . ':'
  153. . $lsmb->{company};
  154. #now set the cookie in the browser
  155. #TODO set domain from ENV, also set path to install path
  156. print qq|Set-Cookie: ${LedgerSMB::Sysconfig::cookie_name}=$newCookieValue; path=$path;\n|;
  157. $lsmb->{LedgerSMB} = $newCookieValue;
  158. $lsmb->{dbh}->commit;
  159. }
  160. sub session_destroy {
  161. my ($form) = @_;
  162. my $login = $form->{login};
  163. $login =~ s/[^a-zA-Z0-9._+\@'-]//g;
  164. # use the central database handle
  165. my $dbh = $form->{dbh};
  166. my $deleteExisting = $dbh->prepare( "
  167. DELETE FROM session
  168. WHERE users_id = (select id from users where username = ?)
  169. " );
  170. $deleteExisting->execute($login)
  171. || $form->dberror(
  172. __FILE__ . ':' . __LINE__ . ': Delete from session: ' );
  173. #delete the cookie in the browser
  174. print qq|Set-Cookie: ${LedgerSMB::Sysconfig::cookie_name}=; path=/;\n|;
  175. }
  176. sub get_credentials {
  177. # Handling of HTTP Basic Auth headers
  178. my $auth = $ENV{'HTTP_AUTHORIZATION'};
  179. $auth =~ s/Basic //i; # strip out basic authentication preface
  180. $auth = MIME::Base64::decode($auth);
  181. my $return_value = {};
  182. ($return_value->{login}, $return_value->{password}) = split(/:/, $auth);
  183. if (defined $LedgerSMB::Sysconfig::force_username_case){
  184. if (lc($LedgerSMB::Sysconfig::force_username_case) eq 'lower'){
  185. $return_value->{login} = lc($return_value->{login});
  186. } elsif (lc($LedgerSMB::Sysconfig::force_username_case) eq 'upper'){
  187. $return_value->{login} = uc($return_value->{login});
  188. }
  189. }
  190. return $return_value;
  191. }
  192. sub credential_prompt{
  193. print "WWW-Authenticate: Basic realm=\"LedgerSMB\"\n";
  194. print "Status: 401 Unauthorized\n\n";
  195. print "Please enter your credentials.\n";
  196. exit;
  197. }
  198. sub password_check {
  199. use Digest::MD5;
  200. my ( $form, $username, $password ) = @_;
  201. $username =~ s/[^a-zA-Z0-9._+\@'-]//g;
  202. # use the central database handle
  203. my $dbh = ${LedgerSMB::Sysconfig::GLOBALDBH};
  204. my $fetchPassword = $dbh->prepare(
  205. "SELECT u.username, uc.password, uc.crypted_password
  206. FROM users as u, users_conf as uc
  207. WHERE u.username = ?
  208. AND u.id = uc.id;"
  209. );
  210. $fetchPassword->execute($username)
  211. || $form->dberror( __FILE__ . ':' . __LINE__ . ': Fetching password : ' );
  212. my ( $dbusername, $md5Password, $cryptPassword ) =
  213. $fetchPassword->fetchrow_array;
  214. if ( $dbusername ne $username ) {
  215. # User data retrieved from db not for the requested user
  216. return 0;
  217. }
  218. elsif ($cryptPassword) {
  219. #First time login from old system, check crypted password
  220. if ( ( crypt $password, substr( $username, 0, 2 ) ) eq $cryptPassword )
  221. {
  222. #password was good, convert to md5 password and null crypted
  223. my $updatePassword = $dbh->prepare(
  224. "UPDATE users_conf
  225. SET password = md5(?),
  226. crypted_password = null
  227. FROM users
  228. WHERE users_conf.id = users.id
  229. AND users.username = ?;"
  230. );
  231. $updatePassword->execute( $password, $username )
  232. || $form->dberror(
  233. __FILE__ . ':' . __LINE__ . ': Converting password : ' );
  234. return 1;
  235. }
  236. else {
  237. return 0; #password failed
  238. }
  239. }
  240. elsif ($md5Password) {
  241. if ( $md5Password ne ( Digest::MD5::md5_hex $password) ) {
  242. return 0;
  243. }
  244. else {
  245. return 1;
  246. }
  247. }
  248. else {
  249. #both the md5Password and cryptPasswords were blank
  250. return 0;
  251. }
  252. }
  253. 1;