From 05333b6f78035da55bdbc52e1bd8fe41dd1ffc07 Mon Sep 17 00:00:00 2001 From: Fabiano FidĂȘncio Date: Jul 14 2017 09:27:19 +0000 Subject: Add "OTP Related Improvements" design page A copy & paste from the old one. Signed-off-by: Fabiano FidĂȘncio --- diff --git a/design_pages/index.rst b/design_pages/index.rst index d2cff66..4210ba9 100644 --- a/design_pages/index.rst +++ b/design_pages/index.rst @@ -63,6 +63,7 @@ When writing a design page, please start from the blank template. one_fourteen_performance_improvements one_way_trusts open_lmi_provider + otp_related_improvements subdomain_configuration shortnames systemd_activatable_responders diff --git a/design_pages/otp_related_improvements.rst b/design_pages/otp_related_improvements.rst new file mode 100644 index 0000000..f907a48 --- /dev/null +++ b/design_pages/otp_related_improvements.rst @@ -0,0 +1,223 @@ +OTP Related Improvements +======================== + +Related Ticket(s): + +- `Investigate using the krb5 responder for driving the PAM + conversation with OTPs `__ +- `Interaction with SSSD, GDM, OTP and GNOME + Keyring `__ + +Problem Statement +----------------- + +One-Time-Passwords (OTP) are typically used as one part of a Two-Factor +authentication (2FA). In most cases the second factor is a long term +password of the user. In general the combined two factors are seen by +the client as an opaque blob which is send together with the user name +to an authentication service with decides if the authentication is +correct or not and returns the result to the client. + +In modern environments there are a number of use cases where only the +long term password factor is needed: + +- offline authentication: 2FA authentication service is not available + and long term password should be compared with the hashed copy +- unlocking key-rings, encrypted devices: the long term password is + used to protect key-rings, files or file-systems; changes of the long + term password should change the encryption key for the other uses as + well + +The most obvious way to get the long term password is to prompt the user +separately for the long term password and the OTP. But for historical +reasons most user interfaces and more important most network protocols +expect a single string as password. While it would be possible to modify +the local user interfaces (graphical and command line) to handle the two +factors separately it is next to impossible to cover all network +protocols. This means we always have to handle the case where both +factors are only available in a single string as a fallback and having +both factors already split will just be a special case. + +It is common practice that when using 2FA with a long term password and +an OTP (mostly generated by a hardware token) the long term password +factor is entered first at the password prompt and then the OTP. In +enterprise environments typically one brand of hardware tokens is used +which means that the OTP factor has a known number of characters. With +this kind of information the combined strings can be split in long term +and OTP factor heuristically. Additionally if the combined string was +split successfully once the size of the OTP factor can be stored in the +cache because in general it will not change and long as the same +hardware token is used. + +If splitting is not possible other consumers of the long term password +should be made aware that they have to request the password on their own +if needed. + +Since OTPs can only be used once SSSD must avoid to use it a second +time. This currently is the case when changing the long term password +via Kerberos. After the password is changed successfully SSSD tries to +get a fresh TGT with the new password. This should not happen in the +case an OTP is used instead the user should be asked to enter a fresh +password (with the new long term password and a valid OTP). + +Overview of the Solution +======================== + +Implementation details +---------------------- + +Removing password with OTP factor from the PAM stack +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If the combined password cannot be split into long term and OTP factor +and new PAM response type should be send back to pam\_sss to indicate +that the combined password should be removed so that other pam modules +(pam-gnome-keyring, pam\_mount) cannot use it anymore and have to +request a password on their own. It might be a good idea to allow an +optional string in this new PAM response. If the password can be split +the string can contain the long term password which should replace the +combined password on the PAM stack. As an alternative an unsigned +integer which indicated where the long term password ends can be used +instead. Then pam\_sss will shorten the combined password to the given +length. + +In sssd-1.12, we will remove the password from the PAM stack when OTP is +used to make sure use-cases like *gnome-keyring* are not broken. We +would need more time for implementation of heuristic and proper testing. +Currently, the *krb5\_child* returns that an OTP was used during +authentication (details in function *parse\_krb5\_child\_response*). +This OTP flag is used just in the function *krb5\_auth\_done*. We will +pass OTP flag to the pam responder (*sssd\_pam*) and from pam responder +to the pam client (*pam\_sss.so*). If the pam client detects that OTP +was used it will remove password from auth\_token. + +Do not request a new TGT after a successful password change +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +In the OTP case asking for a new TGT can easily be skipped in +krb5\_child but this will leave the user with an invalid TGT. A new PAM +response type should indicate that this is the case. It has to be +evaluated if it is possible with PAM to get a fresh authentication of +the user if only a message indicating that the TGT might be invalid and +should be refreshed manually can be send to the user. + +Heuristics +---------- + +There are a number of Heuristics that can be employed depending on the +type of tokens used and whether the type is known or not. + +Hints to split the combined password +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Fixed number of characters in the OTP +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +If the token type is known and has a fixed number of characters then the +client can be simply configured with a hard number and the string +provided by the user simply split counting from the end. knowing the +minimum password length for the actual user password can also allow to +detect errors in entering the credentials (like forgetting to actually +type the OTP) so that a partial input can be discarded immediately. + +For example if we know the OTP is 6 chars and the password policy says +that a password must be at least 8 chars long then an input of +"CoolPassword" would be immediately discarded as it is not at least 14 +chars long (min 8 + 6 for the OTP), while "CoolPassword123456" would be +split in "CoolPassword" and "123456" + +Fixed set of characters in the OTP +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +If it is know that the token's OTP is always only digits then this fact +can be used to split the last part of the string when the exact length +is not known. This heuristic alone is not sufficient as the user +password may contain trailing digits, however it may be combined with +other heuristics to improve them. + +If the length of the OTP is know or is within a small range (for example +only 6 or 8 digit tokens are available) then strings like +"CoolPassword123456" or "CoolPassword1234567" are easy to split. The +first is "CoolPassword"+"123456" the second is "CoolPassword1"+"234567". +A string like "CoolPassword1234T56" would be easy to discard as faulty +as there is a non-digit withing the last 6 chars, however +"CoolPassword12345678 may be split both as "CoolPassword12" "345678" or +"CoolPassword" "12345678" and would need additional heuristics. + +Previous authentication memory +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +If the one shot heuristic fails we can store hints that may allow us to +succeed in successive authentication attempts. If we do not know what is +the token type, length or constants on character types used we can +perform a wild guess as the first authentication attempt by applying a +"most common" guess set and then store a number of hashes that will aid +us in a follow-up attempt. + +For example, we have no knowledge of the token and the user enters +"CoolPassword12345678". We can assume a default heuristic of "6 digits +OTP" and this would split the string in "CoolPassword12" + "345678", +however if we got it wrong and the token was 8 digits long ("12345678") +then we would fail auth and be none the wiser. + +Therefore before sending out the authentication request we gather and +store heuristics of our own in the form of hashes. We will assume that +in a 2FA environment there exist reasonable minimum limits to both the +Password and the OTP length, for example we assume that passwords are +minimum 6 chars long and OTPs are minimum 6 chars long. + +with this assumption we store a hints list of salted hashes of the +following strings: :: + + "CoolPassword12" + "CoolPassword1" + "CoolPassword" + "CoolPasswor" + "CoolPasswo" + "CoolPassw" + "CoolPass" + "CoolPa" + +The order in which the strings are stored on the system may be +intentionally scrambled to prevent faster offline attacks on the shorter +hash. + +If auth succeeds we discard the hints and store only "CoolPassword12" as +an offline password hash. If auth fails we keep the hints for the next +try and just fail authentication (yes even if the Password+OTP was +right). + +On the following authentication attempt we can use the hints to aid us +in properly splitting the OTP. If the user provides us +"CoolPassword19283745" we can try to match it against the hints first +splitting and hashing backwards from longest to shortest. We'll try +"CoolPassword19" and it will fail to match then we'll try +"CoolPassword1" and it will match one of the hints, so we will assume +that as the password and take the remainder (9283745) as the OTP. + +A user mistyping the password on the first attempt may end up causing a +mismatch in a later attempt, we can only clear the previous hints and +fail the auth until the user gets 2 consecutive attempts with different +OTPs right. Once one authentication attempt succeed and we store the +offline password hash we'll have a stronger hint for the future as we'll +have a known good hash. We can also save, as a hint the OTP length, and +check it does not vary in following successful authentication attempts, +if ti varies then we'll change the hint to explicitly list the known +good length used so far as future hints. + +If the user changes its password on a different system or uses multiple +OTP tokens of varying type the hints may not work well. So if an offline +password hash does not match what the user types we need to start from +scratch, and try our best guess as well as save a list of hints. + +This process is not fool proof, but given enough hints (either +discovered or provided as known facts) we could have a system that works +reasonably well. + +How to test +----------- + +Author(s) +~~~~~~~~~ + +Sumit Bose <`sbose@redhat.com `__>