From 534fc2123e40b7517aeaffd709faf72af97ac3b8 Mon Sep 17 00:00:00 2001 From: Dylan Hardison Date: Thu, 5 Nov 2015 00:28:14 -0500 Subject: Bug 1196743 - Fix information disclosure vulnerability that allows attacker to obtain victim's GitHub OAuth return code --- github.cgi | 110 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 110 insertions(+) create mode 100755 github.cgi (limited to 'github.cgi') diff --git a/github.cgi b/github.cgi new file mode 100755 index 000000000..e2955e299 --- /dev/null +++ b/github.cgi @@ -0,0 +1,110 @@ +#!/usr/bin/perl -wT +# 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. + +use 5.10.1; +use strict; +use lib qw(. lib); + +use Bugzilla; +use Bugzilla::Util qw( correct_urlbase ); +use Bugzilla::Error; +use Bugzilla::Constants; +use Bugzilla::Token qw( issue_short_lived_session_token + set_token_extra_data + get_token_extra_data + delete_token ); +use URI; +use URI::QueryParam; +BEGIN { Bugzilla->extensions } +use Bugzilla::Extension::GitHubAuth::Client; + +my $cgi = Bugzilla->cgi; +my $urlbase = correct_urlbase(); + +if (lc($cgi->request_method) eq 'post') { + # POST requests come from Bugzilla itself and begin the GitHub login process + # by redirecting the user to GitHub's authentication endpoint. + + my $user = Bugzilla->login(LOGIN_OPTIONAL); + my $target_uri = $cgi->param('target_uri') or ThrowCodeError("github_invalid_target"); + my $github_secret = $cgi->param('github_secret') or ThrowCodeError("github_invalid_request", { reason => 'invalid secret' }); + my $github_secret2 = Bugzilla->github_secret or ThrowCodeError("github_invalid_request", { reason => 'invalid secret' }); + + ThrowCodeError("github_invalid_request", { reason => 'invalid secret' }) + unless $github_secret eq $github_secret2; + + ThrowCodeError("github_invalid_target", { target_uri => $target_uri }) + unless $target_uri =~ /^\Q$urlbase\E/; + + if ($user->id) { + print $cgi->redirect($target_uri); + exit; + } + + my $state = issue_short_lived_session_token("github_state"); + set_token_extra_data($state, { type => 'github_login', target_uri => $target_uri }); + + $cgi->send_cookie(-name => 'github_state', + -value => $state, + -httponly => 1); + print $cgi->redirect(Bugzilla::Extension::GitHubAuth::Client->authorize_uri($state)); +} +elsif (lc($cgi->request_method) eq 'get') { + # GET requests come from GitHub, with this script acting as the OAuth2 callback. + my $state_param = $cgi->param('state'); + my $state_cookie = $cgi->cookie('github_state'); + + # If the state or params are missing, or the github_state cookie is missing + # we just redirect to index.cgi. + unless ($state_param && $state_cookie && $cgi->param('code')) { + print $cgi->redirect($urlbase . "index.cgi"); + exit; + } + + ThrowCodeError("github_invalid_request", { reason => 'invalid state param' }) + unless $state_param eq $state_cookie; + + my $state_data = get_token_extra_data($state_param); + ThrowCodeError("github_invalid_request", { reason => 'invalid state param' } ) + unless $state_data && $state_data->{type}; + + + $cgi->remove_cookie('github_state'); + delete_token($state_param); + + if ($state_data->{type} eq 'github_login') { + Bugzilla->request_cache->{github_action} = 'login'; + Bugzilla->request_cache->{github_target_uri} = $state_data->{target_uri}; + } + elsif ($state_data->{type} eq 'github_email') { + Bugzilla->request_cache->{github_action} = 'email'; + Bugzilla->request_cache->{github_emails} = $state_data->{emails}; + } + else { + ThrowCodeError("github_invalid_request", { reason => "invalid state param" }) + } + my $user = Bugzilla->login(LOGIN_REQUIRED); + + my $target_uri = URI->new($state_data->{target_uri}); + + # It makes very little sense to login to a page with the logout parameter. + # doing so would be a no-op, so we ignore the logout param here. + $target_uri->query_param_delete('logout'); + + if ($target_uri->path =~ /attachment\.cgi/) { + my $attachment_uri = URI->new($urlbase . "attachment.cgi"); + $attachment_uri->query_param(id => scalar $target_uri->query_param('id')); + if ($target_uri->query_param('action')) { + $attachment_uri->query_param(action => scalar $target_uri->query_param('action')); + } + print $cgi->redirect($attachment_uri); + } + else { + print $cgi->redirect($target_uri); + } +} -- cgit v1.2.3-24-g4f1b