Package: release.debian.org Severity: normal User: [email protected] Usertags: unblock Tags: security Control: affects -1 src:buildbot
Please unblock package buildbot. Version 2.0.1-2 resolves a grave security bug in buildbot (#929849). A source debdiff against 2.0.1-1 follows. Thank you! Robin diff -Nru buildbot-2.0.1/debian/changelog buildbot-2.0.1/debian/changelog --- buildbot-2.0.1/debian/changelog 2019-02-11 21:26:20.000000000 +0100 +++ buildbot-2.0.1/debian/changelog 2019-06-03 14:47:25.000000000 +0200 @@ -1,3 +1,9 @@ +buildbot (2.0.1-2) unstable; urgency=high + + * Fix OAuth module security bypass [CVE-2019-12300] (Closes: #929849) + + -- Robin Jarry <[email protected]> Mon, 03 Jun 2019 14:47:25 +0200 + buildbot (2.0.1-1) unstable; urgency=medium * Use scdoc for man pages diff -Nru buildbot-2.0.1/debian/patches/0005-Revert-master-Accept-GitHub-access_token-directly-fr.patch buildbot-2.0.1/debian/patches/0005-Revert-master-Accept-GitHub-access_token-directly-fr.patch --- buildbot-2.0.1/debian/patches/0005-Revert-master-Accept-GitHub-access_token-directly-fr.patch 1970-01-01 01:00:00.000000000 +0100 +++ buildbot-2.0.1/debian/patches/0005-Revert-master-Accept-GitHub-access_token-directly-fr.patch 2019-06-03 14:47:25.000000000 +0200 @@ -0,0 +1,153 @@ +From: Robin Jarry <[email protected]> +Date: Mon, 3 Jun 2019 14:43:12 +0200 +Subject: Revert "master: Accept GitHub `access_token` directly from user" + +This is a backport of upstream commit e1dcfce4388bfb: ("Revert "master: +Accept GitHub `access_token` directly from user""). + +Fixes: CVE-2019-12300 +Signed-off-by: Pierre Tardy <[email protected]> +Signed-off-by: Robin Jarry <[email protected]> +--- + master/buildbot/test/unit/test_www_oauth.py | 37 +++++--------------------- + master/buildbot/www/oauth2.py | 41 +++++++++++------------------ + 2 files changed, 21 insertions(+), 57 deletions(-) + +diff --git a/master/buildbot/test/unit/test_www_oauth.py b/master/buildbot/test/unit/test_www_oauth.py +index 551f221..fd3b0de 100644 +--- a/master/buildbot/test/unit/test_www_oauth.py ++++ b/master/buildbot/test/unit/test_www_oauth.py +@@ -191,30 +191,7 @@ class OAuth2Auth(www.WwwTestMixin, ConfigErrorsMixin, unittest.TestCase): + 'full_name': 'foo bar'}, res) + + @defer.inlineCallbacks +- def test_GithubAcceptToken(self): +- requests.get.side_effect = [] +- requests.post.side_effect = [ +- FakeResponse(dict(access_token="TOK3N"))] +- self.githubAuth.get = mock.Mock(side_effect=[ +- dict( # /user +- login="bar", +- name="foo bar", +- email="buzz@bar"), +- [ # /user/emails +- {'email': 'buzz@bar', 'verified': True, 'primary': False}, +- {'email': 'bar@foo', 'verified': True, 'primary': True}], +- [ # /user/orgs +- dict(login="hello"), +- dict(login="grp"), +- ]]) +- res = yield self.githubAuth.acceptToken("TOK3N") +- self.assertEqual({'email': 'bar@foo', +- 'username': 'bar', +- 'groups': ["hello", "grp"], +- 'full_name': 'foo bar'}, res) +- +- @defer.inlineCallbacks +- def test_GithubAcceptToken_v4(self): ++ def test_GithubVerifyCode_v4(self): + requests.get.side_effect = [] + requests.post.side_effect = [ + FakeResponse(dict(access_token="TOK3N"))] +@@ -243,14 +220,14 @@ class OAuth2Auth(www.WwwTestMixin, ConfigErrorsMixin, unittest.TestCase): + } + } + ]) +- res = yield self.githubAuth_v4.acceptToken("TOK3N") ++ res = yield self.githubAuth_v4.verifyCode("code!") + self.assertEqual({'email': 'bar@foo', + 'username': 'bar', + 'groups': ["hello", "grp"], + 'full_name': 'foo bar'}, res) + + @defer.inlineCallbacks +- def test_GithubAcceptToken_v4_teams(self): ++ def test_GithubVerifyCode_v4_teams(self): + requests.get.side_effect = [] + requests.post.side_effect = [ + FakeResponse(dict(access_token="TOK3N"))] +@@ -331,7 +308,7 @@ class OAuth2Auth(www.WwwTestMixin, ConfigErrorsMixin, unittest.TestCase): + } + } + ]) +- res = yield self.githubAuth_v4_teams.acceptToken("TOK3N") ++ res = yield self.githubAuth_v4_teams.verifyCode("code!") + self.assertEqual({'email': 'bar@foo', + 'username': 'bar', + 'groups': [ +@@ -434,11 +411,9 @@ class OAuth2Auth(www.WwwTestMixin, ConfigErrorsMixin, unittest.TestCase): + rsrc.auth.verifyCode.assert_called_once_with(b"code!") + self.assertEqual(self.master.session.user_info, {'username': 'bar'}) + self.assertEqual(res, {'redirected': b'://me'}) ++ # token not supported anymore + res = yield self.render_resource(rsrc, b'/?token=token!') +- rsrc.auth.getLoginURL.assert_not_called() +- rsrc.auth.acceptToken.assert_called_once_with(b"token!") +- self.assertEqual(self.master.session.user_info, {'username': 'bar'}) +- self.assertEqual(res, {'redirected': b'://me'}) ++ rsrc.auth.getLoginURL.assert_called_once() + + def test_getConfig(self): + self.assertEqual(self.githubAuth.getConfigDict(), {'fa_icon': 'fa-github', 'autologin': False, +diff --git a/master/buildbot/www/oauth2.py b/master/buildbot/www/oauth2.py +index d2220fd..b57d754 100644 +--- a/master/buildbot/www/oauth2.py ++++ b/master/buildbot/www/oauth2.py +@@ -49,27 +49,24 @@ class OAuth2LoginResource(auth.LoginResource): + @defer.inlineCallbacks + def renderLogin(self, request): + code = request.args.get(b"code", [b""])[0] +- token = request.args.get(b"token", [b""])[0] +- if not token and not code: ++ if not code: + url = request.args.get(b"redirect", [None])[0] + url = yield self.auth.getLoginURL(url) + raise resource.Redirect(url) +- else: +- if not token: +- details = yield self.auth.verifyCode(code) +- else: +- details = yield self.auth.acceptToken(token) +- if self.auth.userInfoProvider is not None: +- infos = yield self.auth.userInfoProvider.getUserInfo(details['username']) +- details.update(infos) +- session = request.getSession() +- session.user_info = details +- session.updateSession(request) +- state = request.args.get(b"state", [b""])[0] +- if state: +- for redirect in parse_qs(state).get('redirect', []): +- raise resource.Redirect(self.auth.homeUri + "#" + redirect) +- raise resource.Redirect(self.auth.homeUri) ++ ++ details = yield self.auth.verifyCode(code) ++ ++ if self.auth.userInfoProvider is not None: ++ infos = yield self.auth.userInfoProvider.getUserInfo(details['username']) ++ details.update(infos) ++ session = request.getSession() ++ session.user_info = details ++ session.updateSession(request) ++ state = request.args.get(b"state", [b""])[0] ++ if state: ++ for redirect in parse_qs(state).get('redirect', []): ++ raise resource.Redirect(self.auth.homeUri + "#" + redirect) ++ raise resource.Redirect(self.auth.homeUri) + + + class OAuth2Auth(auth.AuthBase): +@@ -128,14 +125,6 @@ class OAuth2Auth(auth.AuthBase): + ret = session.get(self.resourceEndpoint + path) + return ret.json() + +- # If the user wants to authenticate directly with an access token they +- # already have, go ahead and just directly accept an access_token from them. +- def acceptToken(self, token): +- def thd(): +- session = self.createSessionFromToken({'access_token': token}) +- return self.getUserInfoFromOAuthClient(session) +- return threads.deferToThread(thd) +- + # based on https://github.com/maraujop/requests-oauth + # from Miguel Araujo, augmented to support header based clientSecret + # passing diff -Nru buildbot-2.0.1/debian/patches/series buildbot-2.0.1/debian/patches/series --- buildbot-2.0.1/debian/patches/series 2019-02-11 21:09:10.000000000 +0100 +++ buildbot-2.0.1/debian/patches/series 2019-06-03 14:47:25.000000000 +0200 @@ -2,3 +2,4 @@ 0002-docs-bundle-sphinxcontrib.jinja-extension.patch 0003-docs-remove-reference-to-external-fonts.patch 0004-master-disable-www-ui-in-sample.cfg.patch +0005-Revert-master-Accept-GitHub-access_token-directly-fr.patch

