Add kx509 client and revamp kx509 service

This commit adds support for kx509 in libkrb5, and revamps the KDC's
kx509 service (fixing bugs, adding features).

Of note is that kx509 is attempted optimistically by the client, with
the certificate and private key stored in the ccache, and optionally in
an external PEM or DER file.

NOTE: We do not optimistically use kx509 in krb5_cc_store_cred() if the
      ccache is a MEMORY ccache so we don't generate a key when
      accepting a GSS context with a delegated credential.

kx509 protocol issues to be fixed in an upcoming commit:

 - no proof of possession (this is mostly not too bad, but we'll want to
   fix it by using CSRs)
 - no algorithm agility (only plain RSA is supported)
 - very limited (no way to request any options in regards to the
   requested cert)
 - error codes are not very useful

Things we're adding in this commit:

 - libkrb5 kx509 client
 - automatic kx509 usage hooked in via krb5_cc_store_cred() of start TGT
 - per-realm templates on the KDC side
 - per-realm issuer certificates
 - send error messages on the KDC side
   (this is essential to avoid client-side timeouts on error)
 - authenticate as many error messages
 - add a protocol probe feature so we can avoid generating a
   keypair if the service is not enabled
   (once we add support for ECC algorithms we won't need this
    anymore; the issue is that RSA keygen is slow)
 - support for different types of client principals, not just username:

    - host-based service and domain-based service, each with its own
      template set per-{realm, service} or per-service

   (the idea is to support issuance of server certificates too, not
    just client/user certs)
 - more complete support for SAN types
 - tests (including that PKINIT->kx509->PKINIT works, which makes it
   possible to have "delegation" of PKIX credentials by just delegating
   Kerberos credentials)
 - document the protocol in lib/krb5/kx509.c

Future work:

 - add option for longer-ticket-lifetime service certs
 - add support for ECDSA, and some day for ed25519 and ed448
 - reuse private key when running kinit
   (this will require rethinking how we trigger optimistic kx509
    usage)
 - HDB lookup for:
    - optional revocation check (not strictly necessary)
    - adding to certificates those SANs listed in HDB
       - hostname aliases (dNSName SANs)
       - rfc822Name (email)
       - XMPP SANs
       - id-pkinit-san (a user could have aliases too)
 - support username wild-card A RRs, ala OSKT/krb5_admin
    i.e., if a host/f.q.d.n principal asks for a certificate for
    some service at some-label.f.q.d.n, then issue it
   (this is not needed at OSKT sites because OSKT already
    supports keying such service principals, which means kx509
    will issue certificates for them, however, it would be nice
    to be able to have this independent of OSKT)
   (a better way to do this would be to integrate more of OSKT
    into Heimdal proper)
 - a kx509 command, or heimtools kx509 subcommand for explicitly
   attempting use of the kx509 protocol (as opposed to implicit, as is
   done in kinit via krb5_cc_store_cred() magic right now)

Issues:

 - optimistically trying kx509 on start realm TGT store -> timeout issues!
    - newer KDCs will return errors because of this commit; older ones
      will not, which causes timouts
    - need a separate timeout setting for kx509 for optimistic case
    - need a [realm] config item and DNS SRV RR lookup for whether a
      realm is expected to support kx509 service
This commit is contained in:
Nicolas Williams
2019-06-17 19:34:21 -05:00
parent 78cb995e6e
commit 6a7e7eace6
23 changed files with 2729 additions and 263 deletions
+122 -6
View File
@@ -1,5 +1,7 @@
-- $Id$
-- The kx509 protocol is documented in RFC6717.
KX509 DEFINITIONS ::=
BEGIN
@@ -12,20 +14,134 @@ KX509-ERROR-CODE ::= INTEGER {
KX509-STATUS-SERVER-TEMP(5),
-- 6 is used internally in the umich client, avoid that
KX509-STATUS-SERVER-KEY(7)
-- Let us reserve 1000+ for Kebreros protocol wire error codes -Nico
}
-- Version 2, which has no proof of possession
Kx509Request ::= SEQUENCE {
authenticator OCTET STRING,
pk-hash OCTET STRING,
pk-key OCTET STRING
pk-hash OCTET STRING, -- HMAC(ticket_session_key, pk-key)
pk-key OCTET STRING -- the public key, DER-encoded (RSA, basically)
}
-- Kx509ErrorCode is a Heimdal-specific enhancement with no change on the wire,
-- and really only just so the error-code field below can fit on one line.
Kx509ErrorCode ::= INTEGER (-2147483648..2147483647)
Kx509Response ::= SEQUENCE {
error-code[0] INTEGER (-2147483648..2147483647)
OPTIONAL -- DEFAULT 0 --,
hash[1] OCTET STRING OPTIONAL,
certificate[2] OCTET STRING OPTIONAL,
error-code[0] Kx509ErrorCode DEFAULT 0,
hash[1] OCTET STRING OPTIONAL, -- HMAC(session_key, ...)
certificate[2] OCTET STRING OPTIONAL, -- Certificates (plural)
-- if client used a
-- Kx509CSRPlus
e-text[3] VisibleString OPTIONAL
}
-- Offset for Kerberos protocol errors when error-code set to one:
kx509-krb5-error-base INTEGER ::= 1000
-- RFC6717 says this about error codes:
--
-- +------------+-----------------------------+------------------------+
-- | error-code | Condition | Example |
-- +------------+-----------------------------+------------------------+
-- | 1 | Permanent problem with | Incompatible version |
-- | | client request | |
-- | 2 | Solvable problem with | Expired Kerberos |
-- | | client request | credentials |
-- | 3 | Temporary problem with | Packet loss |
-- | | client request | |
-- | 4 | Permanent problem with the | Internal |
-- | | server | misconfiguration |
-- | 5 | Temporary problem with the | Server overloaded |
-- | | server | |
-- +------------+-----------------------------+------------------------+
--
-- Looking at UMich CITI's kca (server-side of kx509) implementation, it always
-- sends 0 as the status code, and the UMich CITI kx509 client never checks it.
-- All of these error codes are local only in the UMich CITI implementation.
--
-- Meanwhile, Heimdal used to never send error responses at all.
--
-- As a result we can use whatever error codes we want. We'll send Kerberos
-- protocol errors + 1000. And we'll never use RFC6717 error codes at all.
--
-- Looking at umich source...
--
-- #define KX509_STATUS_GOOD 0 /* No problems handling client request */
-- #define KX509_STATUS_CLNT_BAD 1 /* Client-side permanent problem */
-- /* ex. version incompatible */
-- #define KX509_STATUS_CLNT_FIX 2 /* Client-side solvable problem */
-- /* ex. re-authenticate */
-- #define KX509_STATUS_CLNT_TMP 3 /* Client-side temporary problem */
-- /* ex. packet loss */
-- #define KX509_STATUS_SRVR_BAD 4 /* Server-side permanent problem */
-- /* ex. server broken */
-- #define KX509_STATUS_SRVR_TMP 5 /* Server-side temporary problem */
-- /* ex. server overloaded */
-- #define KX509_STATUS_SRVR_CANT_CLNT_VERS 6 /* Server-side doesn't handle */
-- /* existence of client_version */
-- /* field in KX509_REQUEST */
--
-- The umich server uses these errors in these situations:
--
-- - KX509_STATUS_SRVR_TMP is for:
-- - request decode errors
-- - krb5_is_ap_req() errors
-- - wrong Kerberos protocol vno in AP-REQ
-- - some ENOMEMs
-- - UDP read errors (??)
-- - LDAP issues (they use LDAP to map realm-chopped user princ names to
-- full names)
-- - pk decode errors
-- - KX509_STATUS_CLNT_TMP is for:
-- - HMAC mismatch
-- - some ENOMEMs
-- - failure to accept AP-REQ
-- - failure to unparse princ names from AP-REQ's Ticket
-- - KX509_STATUS_SRVR_BAD is for:
-- - configuration issues (missing issuer creds)
-- - serial number transaction issues (we should randomize)
-- - subjectName construction issues
-- - certificate construction issues (ENOMEM, say)
-- - failure to authenticate (never happens, since KX509_STATUS_CLNT_TMP is
-- used earlier when krb5_rd_req() fails)
-- - KX509_STATUS_CLNT_FIX is for:
-- - more than one component client principals
-- - client princ name component zero string length shorter than 3 or
-- longer than 8 (WTF)
-- - other policy issues
-- - KX509_STATUS_CLNT_BAD
-- - wrong protocol version number (version_2_0)
-- Possible new version designs:
--
-- - keep the protocol the same but use a CSR instead of a raw RSA public key
-- - on the server try decoding first a CSR, then a raw RSA public key
--
-- - keep the protocol the same but use either a CSR or a self-signed cert
-- - on the server try decoding first a Certificate, then a CSR, then a raw
-- RSA public key
--
-- CSRs are a pain to deal with. Self-signed certificates can act as a
-- CSR of a sort. Use notBefore == 1970-01-01T00:00:00Z and an EKU
-- denoting "this certificate is really a much-easier-to-work-with CSR
-- alternative".
--
-- - keep the protocol similar, but use the checksum field of the
-- Authenticator to authenticate the request data; use a KRB-PRIV for the
-- reply
--
-- - extend the KDC/AS/TGS protocols to support certificate issuance, either
-- at the same time as ticket acquisition, or as an alternative
-- - send a CSR as a authz-data element
-- - expect an EncryptedData with the issued Certificate inside as the
-- Ticket in the result (again, ugly hack)
-- - or maybe just add new messages, but, the thing is that the existing
-- "AP-REP + stuff" kx509 protocol is a fine design pattern, there's no
-- need to radically change it, just slightly.
--
-- The main benefit of using an extension to the KDC/AS/TGS protocols is that
-- we could then use FAST for confidentiality protection.
END