389ds / 389-ds-base

The enterprise-class Open Source LDAP server for Linux
https://www.port389.org/
Other
211 stars 93 forks source link

Design: extensible password management #1935

Open 389-ds-bot opened 4 years ago

389-ds-bot commented 4 years ago

Cloned from Pagure issue: https://pagure.io/389-ds-base/issue/48876


In the past, LDAP as the single source of truth was okay for passwords. Everything bound only to LDAP, and we only needed the one hash.

But in the modern world, this is not the case. We have to have passwords in secure hash formats, passwords in alternate formats (consider NTHash for Radius), or even passwords to alter and set kerberos principal data.

We also have businesses that want more policies, and quality checks on passwords.

Our current framework is limited, and has led to the IPA team rolling their own plugin, and password policy management plugin. We can't provide many of these functions from these plugins (Even though we want to!) because they are external to how DS functions.

We also receive many requests about addition of different password quality checking mechanisms. We just can't satisfy them.

As a team, we should investigate how we can re-work our password handling to be:

389-ds-bot commented 4 years ago

Comment from firstyear (@Firstyear) at 2016-06-09 06:24:47

Here is my rough draft, but needs work and comment (and a design document)


We would re-write the password handling to have three stages.

First is a PLUGIN_PASSWORD_POLICY_CHECK_FN, which plugins could register. This returns a LDAP_SUCCESS / anything else. Given the target DN only, this would be to determine if the user can change the password of the target DN, or anything else needed. There could be many stacked policy checks here.

Second is the PLUGIN_PASSWORD_RESTRICTION_CHECK_FN, which again, could be registered. This would be a set of LDAP_SUCCESS / anything checks that given the target DN, and now the password cleartext material, and assesment is made of the password qulatiy and whether the change can proceed. There can be many checks, and they would all have to pass to allow the change. These could be derived from the password policy, so they may be finegrained / subtree policy that could apply for example, or scoping of the checks .... (It's still an idea in my mind, so this will improve.)

Third and finally is a set of PLUGIN_PASSWORD_STORE_FN, which take the cleartext material and write the cleartext through a hash to an attribute. There could be many of these in operation. The benefit to this is we could enable an object to write say:

Then the operation is complete.

389-ds-bot commented 4 years ago

Comment from firstyear (@Firstyear) at 2016-07-07 05:15:10

Another recommendation:

During a bind we should be able to consult multiple password / security modules. This will allow modular MFA or different types of authentication, one time passwords etc.

A hook such as PLUGIN_PASSWORD_BIND_CHECK_FN. The object binding could have a flag to dictate OR or AND behaviour of the plugins associated with the account.

For example, you could then have a PLUGIN_PASSWORD_BIND_CHECK_FN for OTP which takes the last X chars from the password and accepts / rejects the bind. Then the next module is called with the remaining, which is hashed and compared to userPassword.

389-ds-bot commented 4 years ago

Comment from firstyear (@Firstyear) at 2016-08-02 07:14:25

Another hook would be that if the current password hash type != passwordStorageScheme on bind, given we have the clear text password we could do an optional upgrade of the stored hash to the new scheme on bind.

389-ds-bot commented 4 years ago

Comment from firstyear (@Firstyear) at 2017-02-11 22:48:53

Metadata Update from @Firstyear:

389-ds-bot commented 4 years ago

Comment from firstyear (@Firstyear) at 2017-02-15 05:15:09

http://www.port389.org/docs/389ds/design/password-extensibility.html

389-ds-bot commented 4 years ago

Comment from firstyear (@Firstyear) at 2017-02-15 05:15:22

Metadata Update from @Firstyear:

389-ds-bot commented 4 years ago

Comment from firstyear (@Firstyear) at 2017-05-05 01:04:24

Metadata Update from @Firstyear:

389-ds-bot commented 4 years ago

Comment from mreynolds (@mreynolds389) at 2017-10-18 21:57:34

Metadata Update from @mreynolds389:

droideck commented 1 year ago

@Firstyear hi! Do you have any code and/or more work material regarding the topic?

We are about to triage it and the suggestion looks promising, in my opinion...

Firstyear commented 1 year ago

A good example of this is to look at src/plugins/pwdchan/src/pbkdf2.rs (which itself is based on the plugin api and hooks in src/slapi_r_plugin/src/macros.rs )

As you can see, our password plugins can only do three things - declare they are a pwd plugin, "encrypt" aka hash the password and compare a plaintext input against an existing encrypted/hashed password.

It should be obvious from the type signatures in pbkdf2.rs that is is very limiting. You have very limited ability to actually "do" anything with that password, and the hooks are all "inline". I think these are called around about line 800 in ldap/servers/slapd/modify.c

So as a result, you can see how there is no way to "stack" password modules to allow a password change operation to be sent to multiple backends. For example, ldappasswd changes the pw, but today it can only go to one pwstorage module, and it's expected to return it "hashed". Where as something like freeipa would need this to set the password in 389-ds, and in krb, and in ipaNtHash. To achieve this they actually hijack the pwd extop via https://github.com/freeipa/freeipa/blob/master/daemons/ipa-slapi-plugins/ipa-pwd-extop/ipa_pwd_extop.c which itself means ldappasswd will work, but ldapvi and writting the userPassword attribute won't because it bypasses this operation.

Similar the only password policy modules we have are our inbuilt ones - we can't easily add more pwpolicy's and our current policy module is certainly looking quite dated at this point, and very hard to configure. See ldap/servers/slapd/pw.c 1133 .

So the idea here was to make this pluggable. Have a "hook" for stackable pw_syntax/quality modules, so that a module could be written that supports something like zxcbvn. And to have hooks through the password extended operation and change process so that instead of ipa having to hijack the extop, they can hook the password change stack and get a plaintext copy of the password, do the updates they need, and you can have multiple modules "stack" so that 389-ds' internal password change code is still called regardless of call path.

I think a good way to think about this is "how would you write a plugin that intercepts a pw change operation and then stores a secondary hash like ntlm in the entry" and "how would you write a plugin that allows turning off 389-ds pw-policy and to use zxcvbn instead.".