system-based authorization broken in gnome-keyring: NoOptionError: No option 'consumer_key' in section: '1'

Bug #745801 reported by Steve Langasek
100
This bug affects 15 people
Affects Status Importance Assigned to Milestone
launchpadlib
Fix Released
Critical
Brad Crittenden
python-launchpadlib (Ubuntu)
Fix Released
High
Unassigned
Natty
Fix Released
High
Bryce Harrington
Oneiric
Fix Released
High
Bryce Harrington

Bug Description

[Impact]
Some users have reported problems with corrupted keyrings, both in Gnome and KDE, when newlines are included in the password. This makes all launchpadlib-based apps unusable for affected users.

The corruption occurs because launchpadlib attempts to store a multi-line value in the keyring, but the keyring service appears to assume values are single-lined, so chokes when it encounters launchpadlib's entries.

[Workaround]
Delete the keyring (e.g. via 'seahorse') prior to running a launchpadlib-based script.

[Development Fix]
Upstream version 1.9.12 resolves the issue by base64 encoding the serialized value:

- Properly handle Unicode passwords if returned by the keyring.
- Base 64 encode serialized credentials before putting in keyring/wallet.

[Stable Fix]
For the SRU to natty and oneiric, it is proposed to cherrypick the encoding change to credentials.py and its associated test code in test_credential_store.py.

[Test Case]
1. Before installing the new package, use a launchpadlib client to create a key entry in your keyring. It will be called 'network password'. For example, run 'lp-shell' and after authorization examine lp.me.
2. Open the keyring with seahorse and examine the password. It should be plaintext and have 'consumer_key' as one of the first entries. Ensure under 'details' it lists the URL for the Launchpad system you accessed above (i.e., product, staging, or qastaging)
3. Install the updated package.
4. Run the same launchpadlib client. Ensure that authorization is not required, demonstrating that the stored credentials were used.
5. Open seahorse and delete the entry for 'network password'.
6. Run the client again and reauthorize.
7. Examine the key entry in seahorse. This time it should start with '<B64>' and the rest should be base 64 encoded.
8. Finally run the client again and ensure authorization is not required.

[Regression Potential]
Low/none. When there is a corrupted key ring entry, this just reauthorizes and overwrite the old entry, same as if it had expired.

[Original Report]
Running lp:svammel to do a dry run mass bugfiling against LP, I get prompted to authorize my system to connect to launchpad, which is neat. But on a subsequent invocation of the tool, it fails with:

 Traceback (most recent call last):
  File "file-failures.py", line 50, in <module>
    init(args.serviceroot, 'testing', '~/.launchpadlib/cache/')
  File "/home/vorlon/devel/linaro/svammel/config.py", line 96, in init
    set_launchpad(service_root, appid, cachedir)
  File "/home/vorlon/devel/linaro/svammel/config.py", line 72, in set_launchpad
    lp = Launchpad.login_with(appid, root, cachedir)
  File "/usr/lib/pymodules/python2.7/launchpadlib/launchpad.py", line 538, in login_with
    credential_save_failed, version)
  File "/usr/lib/pymodules/python2.7/launchpadlib/launchpad.py", line 341, in _authorize_token_and_login
    authorization_engine.unique_consumer_id)
  File "/usr/lib/pymodules/python2.7/launchpadlib/credentials.py", line 273, in load
    return self.do_load(unique_key)
  File "/usr/lib/pymodules/python2.7/launchpadlib/credentials.py", line 322, in do_load
    return Credentials.from_string(credential_string)
  File "/usr/lib/pymodules/python2.7/launchpadlib/credentials.py", line 89, in from_string
    credentials.load(StringIO(value))
  File "/usr/lib/python2.7/dist-packages/lazr/restfulclient/authorize/oauth.py", line 165, in load
    CREDENTIALS_FILE_VERSION, 'consumer_key')
  File "/usr/lib/python2.7/ConfigParser.py", line 610, in get
    raise NoOptionError(option, section)
 ConfigParser.NoOptionError: No option 'consumer_key' in section: '1'

At James Westby's suggestion, I had a peek inside gnome-keyring with seahorse and found this as the 'password' value of the 'network password' token:

  [1]

Presumably there should be a real password here instead. :)

People experiencing this problem can work around it by opening their gnome keyring, and deleting the broken password.

Related branches

Revision history for this message
Steve Langasek (vorlon) wrote :
Revision history for this message
James Westby (james-w) wrote :

Here's the serialize code for what is stored

        parser.add_section(CREDENTIALS_FILE_VERSION)
        parser.set(CREDENTIALS_FILE_VERSION,
                   'consumer_key', self.consumer.key)
        parser.set(CREDENTIALS_FILE_VERSION,
                   'consumer_secret', self.consumer.secret)
        parser.set(CREDENTIALS_FILE_VERSION,
                   'access_token', self.access_token.key)
        parser.set(CREDENTIALS_FILE_VERSION,
                   'access_secret', self.access_token.secret)
        parser.write(writable_file)

so it executes the first line fine, but the rest don't have the desired
effect.

I'm not sure what would cause the other statements to have
no effect.

The write() must have an effect, otherwise you would get nothing,
so either there is something odd going on with the ConfigParserClass
or we are getting the equivalent of short read/write (the writeable_file
isn't a real file here, just an in memory one, so it's not that exactly)

There is some oddness with a credential_save_failed that could be
involved.

Adding an upstream task for their input.

Thanks,

James

Revision history for this message
Martin Pool (mbp) wrote :

As far as I can tell this means the second run of any lplib client will fail.

Revision history for this message
Martin Pool (mbp) wrote :

parser.write() sent to a stringio does seem to produce the right kind of thing:

'[1]\nconsumer_key = System-wide: Ubuntu (myhostname)\nconsumer_secret = \naccess_token = uuuuuuuuuuuuuuuu\naccess_secret = uuuuuuuuuuuuuuuuuuuuuuuuuuuuuu\n\n'

Apparently the Python keyring module, or something it calls, assumes passwords are a single line and it truncates them at the first \n: if I immediately try to get it back then

keyring.get_password('launchpadlib', 'System-wide: Ubuntu (grace)@https://api.launchpad.net/')
'[1]'

however a simple interactive use of the keyring module doesn't hit this:

In [4]: keyring.set_password('test', 'test', 'bite\nme')

In [5]: keyring.get_password('test', 'test')
Out[5]: 'bite\nme'

In [6]: print _5
bite
me

Revision history for this message
Markus Korn (thekorn) wrote :

I know Leonard has observed some odd behaviour with python-keyring (and esp. the gnomekeyring module) some weeks ago [0], so this might be related.

[0] https://bitbucket.org/kang/python-keyring-lib/issue/40/failures-happen-at-random-points-in-the

Revision history for this message
Martin Pool (mbp) wrote : Re: [Bug 745801] Re: system-based authorization doesn't store useful credentials in gnome-keyring

> [0] https://bitbucket.org/kang/python-keyring-lib/issue/40/failures-
> happen-at-random-points-in-the

I wonder if, for Natty, launchpadlib should disable keyring
integration if it's really as flaky as that post and this bug
suggests.

Curtis Hovey (sinzui)
Changed in launchpadlib:
status: New → Triaged
importance: Undecided → Critical
Revision history for this message
Martin Pool (mbp) wrote : Re: system-based authorization doesn't store useful credentials in gnome-keyring

I think one thing that's happening here is that the keyring gui strongly assumes passwords fit on a single line, and it possibly also implicitly saves them when you close the window. So if you open it up to see what launchpadlib is saving in there, it will break them.

One option would be to just use json or some other serialization that will not include newlines.

Changed in python-launchpadlib (Ubuntu):
status: New → Triaged
importance: Undecided → High
Revision history for this message
Steve Langasek (vorlon) wrote : Re: [Bug 745801] Re: system-based authorization doesn't store useful credentials in gnome-keyring

On Tue, Apr 19, 2011 at 02:10:25AM -0000, Martin Pool wrote:
> I think one thing that's happening here is that the keyring gui strongly
> assumes passwords fit on a single line, and it possibly also implicitly
> saves them when you close the window. So if you open it up to see what
> launchpadlib is saving in there, it will break them.

That's not the root of the problem I'm experiencing. I looked in seahorse
only *because* saving credentials wasn't working right. Any time I use a
launchpadlib-based tool on natty, I have to first open seahorse and delete
the previously-stored invalid credentials, then re-authenticate to openid.
The host-based credentials saving is worse than useless to me at present; at
least if it weren't saving the credentials I could skip the step of opening
seahorse to delete creds.

--
Steve Langasek Give me a lever long enough and a Free OS
Debian Developer to set it on, and I can move the world.
Ubuntu Developer http://www.debian.org/
<email address hidden> <email address hidden>

Revision history for this message
Martin Pool (mbp) wrote : Re: system-based authorization doesn't store useful credentials in gnome-keyring

No, I agree it's not the root cause. I don't know what the real root is.

I will take a quick stab at this by:

- catching and gracefully handling retrieval errors
- storing a new format that is always all on one line

I'm not sure that will fix it, but it seems connected to line breaks so it might.

Revision history for this message
Martin Pool (mbp) wrote :

Many dupes in bzr too, eg bug 762065, and it's biting me every time I use it.

My comment #7 was misguided; I'm sure seahorse isn't breaking it, but also fairly sure something else involved in the stack believes passwords don't contain newlines. Which is pretty reasonable after all.

Revision history for this message
Martin Pool (mbp) wrote :

Testing this is a bit difficult because lazr.restful installs a moderately insane pth file that means you cannot possibly override the

import sys,types,os; p = os.path.join(sys._getframe(1).f_locals['sitedir'], *('lazr',)); ie = os.path.exists(os.path.join(p,'__init__.py')); m = not ie and sys.modules.setdefault('lazr',types.ModuleType('lazr')); mp = (m or []) and m.__dict__.setdefault('__path__',[]); (p not in mp) and mp.append(p)

summary: - system-based authorization doesn't store useful credentials in gnome-
- keyring
+ system-based authorization broken in gnome-keyring: NoOptionError: No
+ option 'consumer_key' in section: '1'
Martin Pool (mbp)
description: updated
Revision history for this message
Steve Langasek (vorlon) wrote :

This problem is still ongoing in oneiric, and to top it all off, seahorse now fails to run at all for me to let me remove the broken key. This makes all launchpadlib apps completely unusable for me.

Revision history for this message
Martin Pool (mbp) wrote : Re: [Bug 745801] Re: system-based authorization broken in gnome-keyring: NoOptionError: No option 'consumer_key' in section: '1'

I have worked around this by deleting the whole keyring. (I actually
wasn't motivated by this bug but rather other bad craziness related to
wifi, but it apparently fixed this.) What a mess. I agree it should
still be fixed.

Revision history for this message
Benji York (benji) wrote :

This and bug 872853 might be related.

Revision history for this message
Francis J. Lacoste (flacoste) wrote :

Escalated by Bryce for the Ubuntu community.

tags: added: escalated
Brad Crittenden (bac)
Changed in launchpadlib:
status: Triaged → In Progress
assignee: nobody → Brad Crittenden (bac)
Brad Crittenden (bac)
Changed in launchpadlib:
status: In Progress → Fix Committed
Revision history for this message
Brad Crittenden (bac) wrote :

PPA with the fix available at https://launchpad.net/~bac/+archive/ppa

Revision history for this message
Fabrice Coutadeur (fabricesp) wrote :

Hi Brad,

With the package from the ppa, I got the following error:
...

Traceback (most recent call last):
  File "/home/fabrice/bin/requestsync", line 336, in <module>
    main()
  File "/home/fabrice/bin/requestsync", line 149, in main
    Launchpad.login(service=options.lpinstance)
  File "/usr/lib/python2.7/dist-packages/ubuntutools/lp/lpapicache.py", line 61, in login
    version=api_version)
  File "/usr/lib/python2.7/dist-packages/launchpadlib/launchpad.py", line 539, in login_with
    credential_save_failed, version)
  File "/usr/lib/python2.7/dist-packages/launchpadlib/launchpad.py", line 342, in _authorize_token_and_login
    authorization_engine.unique_consumer_id)
  File "/usr/lib/python2.7/dist-packages/launchpadlib/credentials.py", line 287, in load
    return self.do_load(unique_key)
  File "/usr/lib/python2.7/dist-packages/launchpadlib/credentials.py", line 339, in do_load
    credentials = Credentials.from_string(credential_string)
  File "/usr/lib/python2.7/dist-packages/launchpadlib/credentials.py", line 102, in from_string
    value = b64decode(value)
  File "/usr/lib/python2.7/base64.py", line 76, in b64decode
    raise TypeError(msg)
TypeError: Incorrect padding

/home/fabrice/bin/requestsync is a link to /usr/bin/requestsync, so it shouldn't comes from there.

Waiting for feedback from others.

Thanks,
Fabrice

Revision history for this message
Brad Crittenden (bac) wrote :

Hi Fabrice,

I changed the way the credentials string is encoded before storing in the keyring while attempting to provide backward compatibility. It looks like the substring I expected to see for an older password was not there, which would have avoided the decode attempt.

Could you use seahorse to open the keyring and look for the 'network password' entry that corresponds to 'api.launchpad.net' and look at the password? After sanitizing your credentials you can add it to the bug report or send it in an encrypted email directly to me if you prefer.

Thanks,
Brad

Revision history for this message
Fabrice Coutadeur (fabricesp) wrote :

Hi Brad,

2011/12/1 Brad Crittenden <email address hidden>:
> Could you use seahorse to open the keyring and look for the 'network
> password' entry that corresponds to 'api.launchpad.net' and look at the
> password?  After sanitizing your credentials you can add it to the bug

I actually don't have any "network password' entry in seahorse (I
checked an old oneiric VM and it's there), so that may be the reason
why it's failing.
I tried to create it manually, but it doesn't seem to let me create a
'network credential' type entry.

and manage-credentials does not exists anymore, so I don't know how to
recreate this entry.

Thanks for your help!

Fabrice

Revision history for this message
Brad Crittenden (bac) wrote :

Fabrice the issue you raise has been filed as bug 900307.

Revision history for this message
Fabrice Coutadeur (fabricesp) wrote :

thanks!

2011/12/5 Brad Crittenden <email address hidden>:
> Fabrice the issue you raise has been filed as bug 900307.
>
> --
> You received this bug notification because you are subscribed to the bug
> report.
> https://bugs.launchpad.net/bugs/745801
>
> Title:
>  system-based authorization broken in gnome-keyring: NoOptionError: No
>  option 'consumer_key' in section: '1'
>
> Status in Launchpad web services client library:
>  Fix Committed
> Status in “python-launchpadlib” package in Ubuntu:
>  Triaged
> Status in “python-launchpadlib” source package in Natty:
>  Triaged
>
> Bug description:
>  Binary package hint: python-launchpadlib
>
>  Running lp:svammel to do a dry run mass bugfiling against LP, I get
>  prompted to authorize my system to connect to launchpad, which is
>  neat.  But on a subsequent invocation of the tool, it fails with:
>
>   Traceback (most recent call last):
>    File "file-failures.py", line 50, in <module>
>      init(args.serviceroot, 'testing', '~/.launchpadlib/cache/')
>    File "/home/vorlon/devel/linaro/svammel/config.py", line 96, in init
>      set_launchpad(service_root, appid, cachedir)
>    File "/home/vorlon/devel/linaro/svammel/config.py", line 72, in set_launchpad
>      lp = Launchpad.login_with(appid, root, cachedir)
>    File "/usr/lib/pymodules/python2.7/launchpadlib/launchpad.py", line 538, in login_with
>      credential_save_failed, version)
>    File "/usr/lib/pymodules/python2.7/launchpadlib/launchpad.py", line 341, in _authorize_token_and_login
>      authorization_engine.unique_consumer_id)
>    File "/usr/lib/pymodules/python2.7/launchpadlib/credentials.py", line 273, in load
>      return self.do_load(unique_key)
>    File "/usr/lib/pymodules/python2.7/launchpadlib/credentials.py", line 322, in do_load
>      return Credentials.from_string(credential_string)
>    File "/usr/lib/pymodules/python2.7/launchpadlib/credentials.py", line 89, in from_string
>      credentials.load(StringIO(value))
>    File "/usr/lib/python2.7/dist-packages/lazr/restfulclient/authorize/oauth.py", line 165, in load
>      CREDENTIALS_FILE_VERSION, 'consumer_key')
>    File "/usr/lib/python2.7/ConfigParser.py", line 610, in get
>      raise NoOptionError(option, section)
>   ConfigParser.NoOptionError: No option 'consumer_key' in section: '1'
>
>  At James Westby's suggestion, I had a peek inside gnome-keyring with
>  seahorse and found this as the 'password' value of the 'network
>  password' token:
>
>    [1]
>
>  Presumably there should be a real password here instead. :)
>
>  People experiencing this problem can work around it by opening their
>  gnome keyring, and deleting the broken password.
>
> To manage notifications about this bug go to:
> https://bugs.launchpad.net/launchpadlib/+bug/745801/+subscriptions

Revision history for this message
Brad Crittenden (bac) wrote :

python-launchpadlib now has a daily build at https://launchpad.net/~launchpad/+archive/ppa which includes the fix for this bug.

Brad Crittenden (bac)
Changed in launchpadlib:
status: Fix Committed → Fix Released
Bryce Harrington (bryce)
Changed in python-launchpadlib (Ubuntu Oneiric):
importance: Undecided → High
status: New → Triaged
Bryce Harrington (bryce)
description: updated
Bryce Harrington (bryce)
description: updated
Bryce Harrington (bryce)
description: updated
Bryce Harrington (bryce)
description: updated
description: updated
Brad Crittenden (bac)
description: updated
Bryce Harrington (bryce)
Changed in python-launchpadlib (Ubuntu Natty):
status: Triaged → In Progress
Changed in python-launchpadlib (Ubuntu Oneiric):
status: Triaged → In Progress
Changed in python-launchpadlib (Ubuntu Natty):
assignee: nobody → Bryce Harrington (bryce)
Changed in python-launchpadlib (Ubuntu Oneiric):
assignee: nobody → Bryce Harrington (bryce)
description: updated
Brad Crittenden (bac)
description: updated
Revision history for this message
Clint Byrum (clint-fewbar) wrote : Please test proposed package

Hello Steve, or anyone else affected,

Accepted python-launchpadlib into oneiric-proposed, the package will build now and be available in a few hours. Please test and give feedback here. See https://wiki.ubuntu.com/Testing/EnableProposed for documentation how to enable and use -proposed. Thank you in advance!

Changed in python-launchpadlib (Ubuntu Oneiric):
status: In Progress → Fix Committed
tags: added: verification-needed
Revision history for this message
Clint Byrum (clint-fewbar) wrote :

Hello Steve, or anyone else affected,

Accepted python-launchpadlib into natty-proposed, the package will build now and be available in a few hours. Please test and give feedback here. See https://wiki.ubuntu.com/Testing/EnableProposed for documentation how to enable and use -proposed. Thank you in advance!

Changed in python-launchpadlib (Ubuntu Natty):
status: In Progress → Fix Committed
Revision history for this message
Brad Crittenden (bac) wrote :

I have verified the natty-proposed and oneiric-proposed packages.

tags: added: verification-done
removed: verification-needed
Revision history for this message
Launchpad Janitor (janitor) wrote :

This bug was fixed in the package python-launchpadlib - 1.9.7-0ubuntu2.1

---------------
python-launchpadlib (1.9.7-0ubuntu2.1) natty-proposed; urgency=low

  * Add 100_base64_encode_credentials.patch cherrypicked from upstream
    tree to fix issue causing launchpadlib scripts to fail due to problems
    associated with keyring corruption. We encode launchpadlib's entries so
    the keyring won't be confused by unexpected characters.
     - Convert credentials from unicode when retrieved from the keyring.
       (LP: #877374)
     - Use base 64 encoding for credentials stored in the keyring.
       (LP: #745801)
     - Properly handle decoding base 64.
       (LP: #900307)
 -- Bryce Harrington <email address hidden> Fri, 09 Dec 2011 15:03:01 -0800

Changed in python-launchpadlib (Ubuntu Natty):
status: Fix Committed → Fix Released
Revision history for this message
Launchpad Janitor (janitor) wrote :

This bug was fixed in the package python-launchpadlib - 1.9.8-2ubuntu0.1

---------------
python-launchpadlib (1.9.8-2ubuntu0.1) oneiric-proposed; urgency=low

  * Add 100_base64_encode_credentials.patch cherrypicked from upstream
    tree to fix issue causing launchpadlib scripts to fail due to problems
    associated with keyring corruption. We encode launchpadlib's entries so
    the keyring won't be confused by unexpected characters.
     - Convert credentials from unicode when retrieved from the keyring.
       (LP: #877374)
     - Use base 64 encoding for credentials stored in the keyring.
       (LP: #745801)
     - Properly handle decoding base 64.
       (LP: #900307)
 -- Bryce Harrington <email address hidden> Fri, 09 Dec 2011 14:55:16 -0800

Changed in python-launchpadlib (Ubuntu Oneiric):
status: Fix Committed → Fix Released
Changed in python-launchpadlib (Ubuntu):
status: Triaged → Fix Released
To post a comment you must log in.
This report contains Public information  
Everyone can see this information.

Other bug subscribers

Remote bug watches

Bug watches keep track of this bug in other bug trackers.