# This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. # # This Source Code Form is "Incompatible With Secondary Licenses", as # defined by the Mozilla Public License, v. 2.0. package Bugzilla::Auth::Login::Cookie; use 5.14.0; use strict; use warnings; use base qw(Bugzilla::Auth::Login); use fields qw(_login_token); use Bugzilla::Constants; use Bugzilla::Error; use Bugzilla::Token; use Bugzilla::Util; use List::Util qw(first); use constant requires_persistence => 0; use constant requires_verification => 0; use constant can_login => 0; sub is_automatic { return $_[0]->login_token ? 0 : 1; } # Note that Cookie never consults the Verifier, it always assumes # it has a valid DB account or it fails. sub get_login_info { my ($self) = @_; my $cgi = Bugzilla->cgi; my $dbh = Bugzilla->dbh; my ($user_id, $login_cookie); if (!Bugzilla->request_cache->{auth_no_automatic_login}) { $login_cookie = $cgi->cookie("Bugzilla_logincookie"); $user_id = $cgi->cookie("Bugzilla_login"); # If cookies cannot be found, this could mean that they haven't # been made available yet. In this case, look at Bugzilla_cookie_list. unless ($login_cookie) { my $cookie = first {$_->name eq 'Bugzilla_logincookie'} @{$cgi->{'Bugzilla_cookie_list'}}; $login_cookie = $cookie->value if $cookie; } unless ($user_id) { my $cookie = first {$_->name eq 'Bugzilla_login'} @{$cgi->{'Bugzilla_cookie_list'}}; $user_id = $cookie->value if $cookie; } # If the call is for a web service, and an api token is provided, check # it is valid. if (i_am_webservice()) { if ($login_cookie && Bugzilla->usage_mode == USAGE_MODE_REST && !exists Bugzilla->input_params->{Bugzilla_api_token}) { # REST requires an api-token when using cookie authentication # fall back to a non-authenticated request $login_cookie = ''; } elsif (Bugzilla->input_params->{Bugzilla_api_token}) { my $api_token = Bugzilla->input_params->{Bugzilla_api_token}; my ($token_user_id, undef, undef, $token_type) = Bugzilla::Token::GetTokenData($api_token); if (!defined $token_type || $token_type ne 'api_token' || $user_id != $token_user_id) { ThrowUserError('auth_invalid_token', { token => $api_token }); } } } } # If no cookies were provided, we also look for a login token # passed in the parameters of a webservice my $token = $self->login_token; if ($token && (!$login_cookie || !$user_id)) { ($user_id, $login_cookie) = ($token->{'user_id'}, $token->{'login_token'}); } my $ip_addr = remote_ip(); if ($login_cookie && $user_id) { # Anything goes for these params - they're just strings which # we're going to verify against the db trick_taint($ip_addr); trick_taint($login_cookie); detaint_natural($user_id); my $db_cookie = $dbh->selectrow_array('SELECT cookie FROM logincookies WHERE cookie = ? AND userid = ? AND (ipaddr = ? OR ipaddr IS NULL)', undef, ($login_cookie, $user_id, $ip_addr)); # If the cookie or token is valid, return a valid username. # If they were not valid and we are using a webservice, then # throw an error notifying the client. if (defined $db_cookie && $login_cookie eq $db_cookie) { # If we logged in successfully, then update the lastused # time on the login cookie $dbh->do("UPDATE logincookies SET lastused = NOW() WHERE cookie = ?", undef, $login_cookie); return { user_id => $user_id }; } elsif (i_am_webservice()) { ThrowUserError('invalid_cookies_or_token'); } } # Either the cookie or token is invalid and we are not authenticating # via a webservice, or we did not receive a cookie or token. We don't # want to ever return AUTH_LOGINFAILED, because we don't want Bugzilla to # actually throw an error when it gets a bad cookie or token. It should just # look like there was no cookie or token to begin with. return { failure => AUTH_NODATA }; } sub login_token { my ($self) = @_; my $input = Bugzilla->input_params; my $usage_mode = Bugzilla->usage_mode; return $self->{'_login_token'} if exists $self->{'_login_token'}; if (!i_am_webservice()) { return $self->{'_login_token'} = undef; } # Check if a token was passed in via requests for WebServices my $token = trim(delete $input->{'Bugzilla_token'}); return $self->{'_login_token'} = undef if !$token; my ($user_id, $login_token) = split('-', $token, 2); if (!detaint_natural($user_id) || !$login_token) { return $self->{'_login_token'} = undef; } return $self->{'_login_token'} = { user_id => $user_id, login_token => $login_token }; } 1;