Bug#929965: unblock: buildbot/2.0.1-2
Package: release.debian.org
Severity: normal
User: release.debian.org@packages.debian.org
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 <robin@jarry.cc> 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 <robin@jarry.cc>
+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 <pierre.tardy@renault.com>
+Signed-off-by: Robin Jarry <robin@jarry.cc>
+---
+ 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
Reply to: