Smart Cards

For Fedora 20 (ended up in 21), we proposed adding support for smart cards to SSSD. This is where we work out how to do it, or try to, anyway.

Multi-step Authentication

Considerations

  • Current sequence of events when a client authenticates:
    • pam_sss sends a request to the PAM responder, containing parameters:
      • PAM_USER (the login name)
      • PAM_SERVICE
      • PAM_TTY
      • PAM_RUSER
      • PAM_RHOST
      • client PID
      • PAM_AUTHTOK (supplied password)
      • PAM_NEWAUTHTOK (new password, if changing)
    • PAM responder sends a D-Bus method call over a private bus to the domain’s provider, containing:
      • user
      • domain
      • service
      • tty
      • ruser
      • rhost
      • authtok_type (enumerated, one of {password, ccfilename, empty})
      • authtok_data
      • newauthtok_type (enumerated, one of {password, ccfilename, empty})
      • newauthtok_data
      • 1 if client is on privileged pipe, else 0
      • client PID
    • domain provider sends a method reply to the PAM responder
      • PAM status code
      • empty array of response messages
    • PAM responder replies to pam_sss
      • PAM status code
  • PAM modules can prompt for an arbitrary number of answers at once.
    • Sometimes this is a password.
    • Sometimes this is a non-password one-off secret.
    • Both tend to be stored as the PAM_AUTHTOK item, without indication of what’s been stored there.
  • An LDAP simple bind transmits the user’s DN and current password to server; nothing else is required.
    • Conclusion: multi-step needs to be a superset of single-step.
  • An LDAP OTP bind requires a fresh OTP value.
    • Conclusion: we need to be able to distinguish between a cacheable password and a not-cacheable not-a-password.
  • Kerberos can prompt for current password, and/or one of several OTP values, and/or one of several smart card PINs.
    • Conclusion: multi-step is sometimes going to mean having multiple questions at a given each step, only some of which will need to be answered before proceeding.
  • The set of things the user can provide may change during an authentication attempt, for example if the user inserts a smart card or swipes a finger over a scanner.
  • The model for the dialog between components should cover these cases.

Proposal

  • Modify request/reply messages from pam_sss to responder to backends.
  • Request and reply messages passed between pam_sss and responder need to add an identifier to distinguish one ongoing authentication attempt from another.
    • This value is under control of a possibly-not-trustworthy pam_sss, so if it isn’t the value that we designate for requesting a new attempt (either 0 or -1), it should be checked for correspondence with an ongoing authentication attempt before we do anything else for it.
  • Request and reply messages passed between responder and data provider need to add an identifier to distinguish one ongoing authentication attempt from another.
    • Because the responder is multiplexing requests from multiple pam_sss instances, this identifier should be not be the same as the one received from pam_sss in a request message.
  • Reply messages over both connections (pam_sss-to-responder, and responder-to-provider) need to be able to carry multiple questions back to a requester. A request message needs to be able to supply answers for any subset of those questions.
  • The initial request also needs to start carrying some flags. If set, the client (which, because PAM conversation callbacks are blocking, probably won’t be pam_sss) is indicating that it can handle updates to the list of questions for a given authentication attempt.
    • The responder can indicate that a smart card was inserted by adding a request for the card’s PIN to the list of questions and sending the new list of questions to the responder’s client.
    • The responder can similarly indicate that input is no longer required if authentication happened out-of-band or has timed out.
    • The provider will likely emit signals or issue method calls to the responder to get the responder to forward updated information to the responder’s client if that information has been asked for.
    • If we get the provider and responder talking at each other in an unsynchronized manner like this, request identifiers are going to have to stay unique over a sufficiently-large period of time that simply discarding previously-queued-but-unprocessed updates will keep clients of the responder from getting confused by a backlog of updates which pertain to a previous authentication attempt.
    • TODO run this by the GDM folks, to make sure it’s the sort of info they’re going to be able to use.
  • Reply messages will need to convey status that isn’t just a PAM result code: { no-such-auth-attempt-id, got-questions-for-you, auth-failed, auth-succeeded }.
  • Suggested prompt layout for reply messages: an array of tuples. [(group,questionid,type,details),…]
    • group: an integer identifier for a group of related questions
    • questionid: integer identifying a specific question in a group
    • type: enumeration indicating “kind” of information being requested
      • password
      • secret (might not be a password, caching not recommended)
      • OTP value
      • insert smart card (unsynchronized only)
      • scan proximity device (unsynchronized only)
      • swipe finger (unsynchronized only)
      • provide smart card PIN out-of-band (if the token has a protected authentication path)
      • smart card PIN (if the token does not have a protected authentication path)
    • detail: structured per-type data:
      • password → empty
      • secret → prompt text
      • OTP value → modeled after ​krb5_responder_otp_tokeninfo
        • service name
        • token index (corresponds to “ti” in the krb5 API)
        • flags
        • format
        • length
        • vendor
        • challenge
        • token ID
        • algorithm ID
      • insert smart card -> empty
      • scan proximity device -> empty
      • swipe finger -> empty
      • provide smart card PIN out-of-band or smart card PIN
        • either broken-out ​pkinit_identities
          • module name (shared library file name)
          • slot ID (hex string representing byte array)
          • token label (string)
          • certificate ID (hex string representing byte array)
          • certificate label (string)
        • or ​p11-kit URI fields, some of which are:
          • token label
          • token manufacturer
          • token model
          • token serial number
          • certificate label
        • We’ll have to go with a common subset of the two to mask the differences between what we get when we’re doing PKINIT and what we have available when we’re calling p11-kit/PKCS#11 directly. Medium-to-longer term, we may need to add to what PKINIT provides here.
    • The responder needs to be able to filter the list of questions it gets from the provider to remove questions for kinds of data which the administrator does not want to allow to be used for authenticating users, passing back to the client only a subset of the questions that the provider requested be asked. The list of questions could even be pared down to nothing.
  • Suggested answer form for request messages: another tuple array. [(group,questionid,answer)…]
    • For any group, all questionids are expected to have answers.
    • Answer formats:
      • password → text string
      • secret → text string
      • OTP value → (token index, otp, pin) (or is text string enough?)
      • insert smart card (never sent by client - out-of-band action)
      • scan proximity device (never sent by client - out-of-band action)
      • swipe finger (never sent by client - out-of-band action)
      • provide smart card PIN out-of-band (never sent by client - out-of-band action)
      • smart card PIN → (token label, text string)

Smart Card support, part 1: load the drivers, find the right reader (if we can).

Considerations

  • Readers (slots in PKCS#11 jargon), and access to them, haven’t historically been tied to a console or seat.
  • Cards (tokens in PKCS#11 jargon) have therefore been accessible to any user on the system for the purposes of logging in to a card and using it, or attempting to log in to a card.
  • Some cards self-destruct after a maximum number of failed login attempts. Some of these cards can expose a flag in their CK_TOKEN_INFO to warn that this is about to become a problem. Experimentally, some cards which lock the user out after some number of failed login attempts, however, don’t expose this flag.
  • Multi-user systems, or software which isn’t careful about what it uses to try to log into a card, can make it very easy for one user to destroy someone else’s card.

Proposal

  • Use p11-kit to avoid having to tell SSSD specifically about which module or modules to use, and to allow us to share the hardware configuration which will be used by the user during their login session. For PKINIT, this means we’ll probably end up using p11-kit-proxy.so by default, as it expects the name of a module to load when using PKCS#11.
  • Loading the p11-kit-proxy.so module using NSS’s APIs gives it access to the same set of modules that p11-kit’s native API provides, and also adds reference counting for module initializations, which should avoid at least one known error case that we’ve seen with the soft-pkcs11.so module (wherein calling its initialization function a second time nukes any still-being used state).
  • TODO We can map from a responder client PID to a unit which might have a TTY (before login) or a session with a seat (after login), but can we map from a SLOT_INFO to a anything that lets us avoid using a slot if it doesn’t belong to the seat on which a particular client sits? (This bit has been bugging the author for a while now.)

Smart Card support, part 2: verify the card’s contents.

Considerations

  • We need to log in to the card as a user.
  • We need to find a certificate on the card for which the card also holds the corresponding private key.
    • Conceptually, the simplest thing is to just sign some data with the private key, and verify it using the public key in the certificate.
    • We could alternately read the specific public fields from the private key, pull the public key out of the certificate, decode that public key (in a manner specific to the type of key), and compare the two, but we still wouldn’t know for sure that the private parts of the key were correct. And we’d have extend this code for every new type of key pair we wanted to support into the future. So we’ll just do the sign/verify, like pam_pkcs11 does.
  • We need to verify that that certificate is issued by an issuer who is trusted to issue certificates for login. The set of CAs which we trust to do that is almost certainly going to be a much smaller set than the full set of commercial CAs that we trust for issuing SSL server certificates.
  • We need to verify that that certificate is suitable for login, i.e., not just for signing email and/or visiting web sites. On Windows KDCs, and on MIT KDCs by default, this is indicated with a particular value in the extendedKeyUsage extension. Large client rollouts may have deployed cards with certificates containing one or the other or neither, so this needs to be a requirement that we can relax through configuration.

Proposal

  • If configured, if a card is not present, wait for card insertion in to a suitable slot. Note that the slot and token may show up at the same time.
  • Verify that user has access to token in slot.
    • PIN is accepted for login to card as CKU_USER.
    • Find certificate and private key where the pair is marked as suitable for signing data.
    • Generate random to-be-signed data.
    • Sign generated data with private key.
    • Verify signature over generated data using public key contained in certificate.
  • Verify that the just-used certificate on the token chains up to an issuer who is trusted to issue certificates which can be used for logging in.
    • Note that this trusted issuer set is tightly controlled beyond the normal set of CAs who are trusted to issue certificates for other servers.
    • TODO seek guidance and assistance from the p11-kit implementors, who are working on standardizing the storage and expression of trust anchor information. PKINIT expects that we pass in the names of files containing trusted anchors and known intermediates, while p11-kit/PKCS#11 expose the information as certificate and CKO_NSS_TRUST/??? objects on a token. We’re going to want to use p11-kit as much as possible cut down on the number of places this has to be configured on a given system.
  • Check for either the Windows Smart Card Logon extendedKeyUsage value, or the RFC4556 Kerberos Client value, but keep what exactly we look for configurable for the sake of existing deployments.

Smart Card support, part 3: check that the card matches the account.

Considerations

  • A certificate contains a subject field which identifies the owner of the public key in the certificate. This is a distinguished name, similar in many ways to an LDAP DN, mainly differing in that it’s encoded as a DER blob, with attribute types represented by OIDs rather than by name, and with values encoded as DER strings of one of several types.
  • A certificate can also contain one or more subjectAlternativeName extension values. If it contains any of these, the subject field becomes optional. Each subjectAlternativeName (SAN) value is considered equally canonical. SAN values can take multiple forms, including but not limited to distinguished names, DNS names, IP addresses, email addresses, and Kerberos principal names.
  • Somehow, we have to use this naming information to figure out if the certificate belongs to the account.
  • For tech demo situations, we’d like to be able to use this information to discover the user account name without requiring that it be specified by an end-user. It’s a secondary concern, however, and some customers legitimately want to be able to turn such a feature off.

Proposal

Per-provider check for binding between certificate and account.

  • Kerberos (likely AD and IPA as well): let the KDC decide - if we get creds for the account’s principal, we’re done.
    • This should handle matching using altSecurityIdentity information for us if we’re talking to an AD server.
    • TODO MIT krb5 KDC PKINIT logic currently only accepts a client’s certificate if it contains the principal name as a SAN value. We’ll want to be able to extend that.
    • Don’t forget that we need to verify the KDC’s certificate, and its suitability to be issuing Kerberos tickets for the realm of the account.
  • LDAP (possibly AD and IPA as well):
    • Let the server decide — connect to server using TLS with client auth and SASL/EXTERNAL, use whoami EXOP to read our entry DN, compare to the account’s DN.
      • With NSS, this should only require setting LDAP_OPT_X_TLS_CERTFILE to “tokenname:certnickname”, preferably after logging in to token.
      • This should get the server to handle altSecurityIdentity matching for us if we’re talking to an AD server.
      • When we start issuing client certificates in FreeIPA, we’ll need to make sure that FreeIPA’s configuring dirsrv’s certmap.conf to pull the right attribute from the subject DN and construct a search that will match the user’s entry.
      • TODO figure out if there’s a way to take the CK_FUNCTION_LIST which p11-kit/PKCS#11 gives us and hand it to the copy of NSS that libldap is using under the covers.
    • Let the server merely hold the info — search for an entry that contains a copy of the certificate that’s on the token as a userCertificate value, check results.
    • Implement something like certmap.conf logic at the client.
    • Compare the subject/SAN DN to the user’s entry’s DN.
      • This is trickier than it sounds: converting from the binary encoding used in the certificate’s fields to the text format used by LDAP requires that we map the OIDs of attributes to the name of the naming attribute used in the LDAP DN. Converting the LDAP DN to binary form may work better, but that’s just a guess.
    • Don’t forget that we need to verify the server’s certificate.
  • Local files:
    • Read certificate Kerberos principal name SAN and perform libkrb5 auth_to_local mapping.
    • Check for the certificate in the user’s entry in a local data store.
    • Check for the public key in user’s SSH authorized key list.
    • Check for certificate subject/SAN CN matching user GECOS.
    • Check for certificate subject/SAN CN matching user name.
    • Check for certificate subject/SAN UID matching user name.
    • Check for certificate subject/SAN emailAddress matching user name with in a configured email domain.