AccountManager: erratic behaviour of ResetPwStore

33 views
Skip to first unread message

Nicolas MARTIN

unread,
Sep 5, 2017, 8:40:01 AM9/5/17
to Trac Users
Hello Trac users,


Few months ago, we moved in our project from HTTP to HTML authentication
with AccountManager. No particular issue since then except a warning in
the log "Trac[web_ui] WARNING: ResetPwStore is disabled, therefore
password reset won't work." but with no evident effect ('ResetPwStore'
was still enabled under 'Plugins' and the password reset procedure was
working).

But recently several users got the following error at the login page
with the temporary password received by email, when they tried to set
their own password after the account creation:

Traceback (most recent call last):
File "/usr/lib/python2.7/site-packages/trac/web/api.py", line 514, in send_error
data, 'text/html')
File "/usr/lib/python2.7/site-packages/trac/web/chrome.py", line 968, in render_template
message = Markup(req.session.pop('chrome.%s.%d'
File "/usr/lib/python2.7/site-packages/trac/web/api.py", line 316, in __getattr__
value = self.callbacks[name](self)
File "/usr/lib/python2.7/site-packages/trac/web/main.py", line 268, in _get_session
return Session(self.env, req)
File "/usr/lib/python2.7/site-packages/trac/web/session.py", line 200, in __init__
if req.authname == 'anonymous':
File "/usr/lib/python2.7/site-packages/trac/web/api.py", line 316, in __getattr__
value = self.callbacks[name](self)
File "/usr/lib/python2.7/site-packages/trac/web/main.py", line 135, in authenticate
authname = authenticator.authenticate(req)
File "build/bdist.linux-x86_64/egg/acct_mgr/util.py", line 81, in wrap
return func(self, *args, **kwds)
File "build/bdist.linux-x86_64/egg/acct_mgr/web_ui.py", line 451, in authenticate
username = self._remote_user(req)
File "build/bdist.linux-x86_64/egg/acct_mgr/web_ui.py", line 766, in _remote_user
if acctmgr.check_password(username, password) is True:
File "build/bdist.linux-x86_64/egg/acct_mgr/api.py", line 287, in check_password
valid = store.check_password(user, password)
File "build/bdist.linux-x86_64/egg/acct_mgr/htfile.py", line 69, in check_password
return self._check_userline(user, password, line)
File "build/bdist.linux-x86_64/egg/acct_mgr/htfile.py", line 207, in _check_userline
return suffix == htpasswd(password, suffix)
File "build/bdist.linux-x86_64/egg/acct_mgr/pwhash.py", line 140, in htpasswd
available."""))
NotImplementedError: Neither are "sha2" hash algorithms supported by the
"crypt" module on this platform nor is "passlib"
available.

The tricky thing is that at the same time other users did not encounter
this issue and were able to complete the procedure, I received few email
notifications of password reset since the first feedback of the problem.

This occurred during my vacations and at first glance I does not have to
blame my colleagues of a change in the plugin configuration.
Regarding the plugin version, we use the very last revision of the trunk
branch (r16720) on our Trac 1.0.1 installation.

Here is an extract of trac.ini

[account-manager]
htpasswd_file = ../auth/trac.passwd
htpasswd_hash_type = sha512
login_attempt_max_count = 3
notify_actions = new,change,delete
password_store = HtPasswdStore
refresh_passwd = true
user_lock_time = 3600
username_regexp = (?i)^[a-z0-9]{5,}$

...

[components]
acct_mgr.admin.useradminpanel = disabled
acct_mgr.db.sessionstore = disabled
acct_mgr.htfile.htdigeststore = disabled
acct_mgr.http.* = disabled
acct_mgr.register.* = disabled
acct_mgr.svnserve.svnservepasswordstore = disabled
trac.web.auth.loginmodule = disabled
tracopt.ticket.commit_updater.* = enabled
tracopt.ticket.deleter.ticketdeleter = disabled
tracopt.versioncontrol.svn.svn_fs.subversionconnector = enabled
xmail.xmailmainview.xmailmainview = enabled
xmail.xmailpermissions.xmailpermission = enabled


I was able to reproduce the bug by myself with my personal account, then
I installed passlib module and finally add 'ResetPwStore' to
'password_store' option (remove the warning in the Trac log) but the
issue remains...



Regards,
Nicolas

RjOllos

unread,
Sep 5, 2017, 3:11:29 PM9/5/17
to Trac Users, ntm...@locean.upmc.fr
Try adding:
[account-manager]
hash_method = HtPasswdHashMethod

What Linux distro are you running?

What does the following yield on your system?
>>> from passlib.apps import custom_app_context as passlib_ctxt
>>> passlib_ctxt.policy.schemes()
['sha512_crypt', 'sha256_crypt']

- Ryan

Nicolas MARTIN

unread,
Sep 5, 2017, 4:29:38 PM9/5/17
to RjOllos, Trac Users
Done.
No apparent change, the reset procedure continues to end prematurely.


What Linux distro are you running?

Mageia 3 (fork of former Mandriva, RHEL derivative), the OS is as up-to-date as our Trac installation...


What does the following yield on your system?
>>> from passlib.apps import custom_app_context as passlib_ctxt
>>> passlib_ctxt.policy.schemes()
['sha512_crypt', 'sha256_crypt']

Seems to be a dead-end because passlib was not installed when we initially moved to AccountManager to handle the server authentication.
Anyway, I have installed passlib-1.7.1 and I got what we could expect:

$ python
Python 2.7.6 (default, Mar 18 2014, 22:18:46)
[GCC 4.8.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.

>>> from passlib.apps import custom_app_context as passlib_ctxt
>>> passlib_ctxt.policy.schemes()
['sha512_crypt', 'sha256_crypt']

- Ryan

For my part, I was able to reset my user password from the administration interface without problem.
Also I realized that the login module crashes in all cases (void, wrong or temporary password from email) except of course the good one from the hash passwords file.

Nicolas

RjOllos

unread,
Sep 5, 2017, 4:45:13 PM9/5/17
to Trac Users, rjo...@gmail.com, ntm...@locean.upmc.fr


On Tuesday, September 5, 2017 at 1:29:38 PM UTC-7, Nicolas MARTIN wrote:
What does the following yield on your system?
>>> from passlib.apps import custom_app_context as passlib_ctxt
>>> passlib_ctxt.policy.schemes()
['sha512_crypt', 'sha256_crypt']

Seems to be a dead-end because passlib was not installed when we initially moved to AccountManager to handle the server authentication.
Anyway, I have installed passlib-1.7.1 and I got what we could expect:

$ python
Python 2.7.6 (default, Mar 18 2014, 22:18:46)
[GCC 4.8.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> from passlib.apps import custom_app_context as passlib_ctxt
>>> passlib_ctxt.policy.schemes()
['sha512_crypt', 'sha256_crypt']

We needed to be sure that the hashing algorithms are available on your system:

This class will use the first available of two possible backends:

    stdlib crypt(), if the host OS supports SHA256-Crypt (most Linux systems).
    a pure python implementation of SHA256-Crypt built into Passlib

Take a look at the code and there are only about 3 conditions that would lead to not taking the desired branch:

From the information we have the hashes must not have the proper prefix.
 

For my part, I was able to reset my user password from the administration interface without problem.
Also I realized that the login module crashes in all cases (void, wrong or temporary password from email) except of course the good one from the hash passwords file.

Nicolas


There are multiple problems with password reset. AccountManager hasn't been fully adapted to account for username caching in Trac 1.0.2+.

- Ryan

RjOllos

unread,
Sep 5, 2017, 5:07:57 PM9/5/17
to Trac Users, rjo...@gmail.com, ntm...@locean.upmc.fr


On Tuesday, September 5, 2017 at 1:45:13 PM UTC-7, RjOllos wrote:
There are multiple problems with password reset. AccountManager hasn't been fully adapted to account for username caching in Trac 1.0.2+.

- Ryan

Regarding #11869, I'm planning to spend some time on that at the end of this month. I think password reset on trac-hacks.org may have some issues, and it's been on my todo list for a long time.

If you can't sort out the issue, please keep an eye on #11869, and if possible, retest when more changes are committed.

- Ryan 

Nicolas MARTIN

unread,
Sep 5, 2017, 6:09:40 PM9/5/17
to RjOllos, Trac Users



On 05/09/2017 22:45, RjOllos wrote:


On Tuesday, September 5, 2017 at 1:29:38 PM UTC-7, Nicolas MARTIN wrote:
What does the following yield on your system?
>>> from passlib.apps import custom_app_context as passlib_ctxt
>>> passlib_ctxt.policy.schemes()
['sha512_crypt', 'sha256_crypt']

Seems to be a dead-end because passlib was not installed when we initially moved to AccountManager to handle the server authentication.
Anyway, I have installed passlib-1.7.1 and I got what we could expect:

$ python
Python 2.7.6 (default, Mar 18 2014, 22:18:46)
[GCC 4.8.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> from passlib.apps import custom_app_context as passlib_ctxt
>>> passlib_ctxt.policy.schemes()
['sha512_crypt', 'sha256_crypt']

We needed to be sure that the hashing algorithms are available on your system:

$ python
Python 2.7.6 (default, Mar 18 2014, 22:18:46)
[GCC 4.8.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.

>>> from passlib.hash import sha256_crypt
>>> hash = sha256_crypt.hash("password")
>>> hash
'$5$rounds=535000$9a9Y6OmJzEwN0hcM$VmN.XKy2IezFueRtMYxuDx8FLizvzVLlcpCXzoOFjM9'
>>> sha256_crypt.using(rounds=12345).hash("password")
'$5$rounds=12345$3GW9pFS/06AYchFb$nDYB5uiDrsiD7XHxbeUVpHHGvm.SUrPMPVIVYBto/Q.'
>>> sha256_crypt.verify("password", hash)
True
>>> sha256_crypt.verify("letmein", hash)
False
>>> from passlib.hash import sha512_crypt
>>> hash = sha512_crypt.hash("password")
>>> hash
'$6$rounds=656000$0EDxNq0bt17MlHHU$81oKrH4iCIg7q3hpBLatjrUxcKSXVue.srBcU98TA6LWo2LKklAxMeOeuxwLT82AKo7Ti2oSHDe8VQp6Tfn14/'
>>> sha512_crypt.using(rounds=12345).hash("password")
'$6$rounds=12345$ouWDfvOA6Ur6feP1$pfnUTWUHaIbvRXTmju1F1zZ/MzuNOFVIyu8yTUYkDltlI.46GFH/DfQtYlZjrrr2Xh3B.eysNI7oZiJxKDEVS0'
>>> sha512_crypt.verify("password", hash)
True
>>> sha512_crypt.verify("letmein", hash)
False


This class will use the first available of two possible backends:

    stdlib crypt(), if the host OS supports SHA256-Crypt (most Linux systems).
    a pure python implementation of SHA256-Crypt built into Passlib

Take a look at the code and there are only about 3 conditions that would lead to not taking the desired branch:

From the information we have the hashes must not have the proper prefix.

$ grep -o ':\$[^$]*' ~/auth/trac.passwd | sort | uniq -c
   1428 :$1
     182 :$6
         3 :$apr1

'$1' hash passwords are from the previous process with Apache authentication, '$apr1' few testing accounts before we realized with the server administrator that the default hash type 'apr_md5_crypt' will not work to access our SVN repository controlled by a Perl script with crypt() function. Thus we set the hash type to sha512 so '$6' represents new or updated passwords.

I still have a backup of my passwords file just before the switch with all former '$1' hash passwords. Just in case...

$ grep -o ':\$[^$]*' ~/auth/save/trac.passwd.save | sort | uniq -c
   1598 :$1

RjOllos

unread,
Sep 5, 2017, 6:36:58 PM9/5/17
to Trac Users, rjo...@gmail.com, ntm...@locean.upmc.fr


On Tuesday, September 5, 2017 at 3:09:40 PM UTC-7, Nicolas MARTIN wrote:
From the information we have the hashes must not have the proper prefix.

$ grep -o ':\$[^$]*' ~/auth/trac.passwd | sort | uniq -c
   1428 :$1
     182 :$6
         3 :$apr1

'$1' hash passwords are from the previous process with Apache authentication, '$apr1' few testing accounts before we realized with the server administrator that the default hash type 'apr_md5_crypt' will not work to access our SVN repository controlled by a Perl script with crypt() function. Thus we set the hash type to sha512 so '$6' represents new or updated passwords.

I still have a backup of my passwords file just before the switch with all former '$1' hash passwords. Just in case...

$ grep -o ':\$[^$]*' ~/auth/save/trac.passwd.save | sort | uniq -c
   1598 :$1

 
I assume you wouldn't see the traceback if you changed your password to sha512 and then tried to change it again. That is, the issue is only seen when changing from a MD5 to sha512 password.

You could try the following patch:
Index: acct_mgr/pwhash.py
===================================================================
--- acct_mgr/pwhash.py    (revision 16783)
+++ acct_mgr/pwhash.py    (working copy)
@@ -113,6 +113,8 @@

     if hash.startswith('$apr1$'):
         return md5crypt(password, hash[6:].split('$')[0], '$apr1$')
+    elif hash.startswith('$1$'):
+        return md5crypt(password, hash[3:].split('$')[0])
     elif hash.startswith('{SHA}'):
         return '{SHA}' + hashlib.sha1(password).digest().encode('base64')[:-1]
     elif passlib_ctxt is not None and hash.startswith('$5$') and \

- Ryan
 

Nicolas MARTIN

unread,
Sep 6, 2017, 11:46:52 AM9/6/17
to RjOllos, Trac Users

I've modified pwhash.py but I'm still faced the issue.

Now I'm trying to analyse this in a different way, to see why the procedure doesn't work now (changes in Trac or the server) or if the problem dates back to the authentication switch. In particular, I found that most of the 'password_reset' entries for newly accounts are still in the 'session_attribute' SQL datatable. Would they not have been removed after the first connection with the personalized password ?

The situation is a bit critical because I still can create an account but for me I have no more a secure way to transmit the access. Any hint ?


Nicolas

RjOllos

unread,
Sep 6, 2017, 5:06:03 PM9/6/17
to Trac Users, rjo...@gmail.com, ntm...@locean.upmc.fr


On Wednesday, September 6, 2017 at 8:46:52 AM UTC-7, Nicolas MARTIN wrote:

I've modified pwhash.py but I'm still faced the issue.

Now I'm trying to analyse this in a different way, to see why the procedure doesn't work now (changes in Trac or the server) or if the problem dates back to the authentication switch. In particular, I found that most of the 'password_reset' entries for newly accounts are still in the 'session_attribute' SQL datatable. Would they not have been removed after the first connection with the personalized password ?


As noted, there are several issues with password reset. I doubt it works properly in all circumstances. I will try to take a look in the next few days.
 

The situation is a bit critical because I still can create an account but for me I have no more a secure way to transmit the access. Any hint ?


I don't understand the last statement.

- Ryan

Nicolas MARTIN

unread,
Sep 6, 2017, 7:16:50 PM9/6/17
to RjOllos, Trac Users, rjo...@gmail.com
For security purpose, I use the reset password procedure for every new account.
A new user contacts us by email with a chosen username, then I create the new account (with only email and username, random password is generated), and ask him/her back to follow the reset procedure. Finally the user has to set its own password after the first connection.
Following this, I presume to reduce drastically the vulnerability of the whole process. The credentials of an account will only be contained in a single email just once and valid for a very limited time. Usually the user will finalize the password reset in a few seconds.

At this time, I have a few people with a valid account but they can't use it. On top of that, as the repository access is based on the same passwords file, they are even not allowed to download the source code of our project.
I'm a bit paranoid perhaps but I don't want to send clear password if I'm not sure it will be changed right away.

Nicolas

Nicolas MARTIN

unread,
Sep 7, 2017, 12:27:01 PM9/7/17
to RjOllos, Trac Users
Well good news ! I found the origin of our issue but probably the best
people to explain it would be the plugin developers.

On the administration interface of the user accounts
(/admin/accounts/users), you can create an account with 2 different
forms: 'Add user' at the top of the page or 'Add external user' at the
bottom (which in fact are duplicated in our project but it is an another
story). The evident difference between them is the setting of the
password (random or defined) but I didn't pay to much attention on this
because there is no particular information in the wiki and everything
was fine at that time.

So from the beginning I had to use 'Add user' form for the reasons I
explained in my last message. The newly account was listed under 'Users'
table then moved to the 'External Users' table after the first
connection of the user.
During my vacations, my colleague was not sure which form to use. She
saw that nearly all accounts were listed in 'External users' so she
chose 'Add external user' form which was consistent. Today I give it a
try to create a user account as usual ('Add user' form) and it works
perfectly...

That's all for the visible part of the problem, I was guessing that the
2 forms followed more or less the same process but it is not the case.
'Users' table seems to refer to accounts in the Trac database and
'External users' to accounts in a password file. So my initial procedure
of using 'Add user' jointly with HtPasswdStore is probably not the good
one but it works.


Nicolas
Reply all
Reply to author
Forward
0 new messages