Bug#929965: unblock: buildbot/2.0.1-2

classic Classic list List threaded Threaded
2 messages Options
Reply | Threaded
Open this post in threaded view
|

Bug#929965: unblock: buildbot/2.0.1-2

Robin Jarry
Package: release.debian.org
Severity: normal
User: [hidden email]
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 <[hidden email]>  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 <[hidden email]>
+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 <[hidden email]>
+Signed-off-by: Robin Jarry <[hidden email]>
+---
+ 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 | Threaded
Open this post in threaded view
|

Bug#929965: unblock: buildbot/2.0.1-2

Jonathan Wiltshire via nm
On Tue, Jun 04, 2019 at 02:30:19PM +0200, Robin Jarry wrote:
> Please unblock package buildbot.
>
> Version 2.0.1-2 resolves a grave security bug in buildbot (#929849).

Unblocked; thanks.

--
Jonathan Wiltshire                                      [hidden email]
Debian Developer                         http://people.debian.org/~jmw

4096R: 0xD3524C51 / 0A55 B7C5 1223 3942 86EC  74C3 5394 479D D352 4C51