Refactor and enhance TGT forwarding to allow forwarding of leaf
(destination) TGTs for selected destination realms.
Enhance kinit(1) to renew non-origin realm tickets
Document delegate-destination-tgt
Use the newly implemented _krb5_mk_1cred().
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
* Anonymous pkinit responses from the KDC where the name
type is not well-known (as issued by 7.5 KDCs and earlier)
are accepted by the client. There is no need for the client
to strictly enforce the name type.
* With historical_anon_pkinit = true, the kinit(1) client's
"--anonymous" option only performs anon pkinit, and does
not require an '@' prefix for the realm argument.
* With historical_anon_realm = true, the KDC issues anon
pkinit tickets with the legacy pre-7.0 "real" realm.
When new keys are added (typically via kadm5_setkey_principal_3),
truncate the key history to remove old keys, that is keys older than
the newest key which was in effect prior longer ago than the principal's
maximum ticket lifetime. This feature is controlled via the "[kadmin]"
section's "prune-key-history" boolean parameter, which defaults to false.
Currently this happens only when kadm5_setkey_principal_3()
is called directly on the server, the client API simulates
kadm5_setkey_principal_3() via a get, update, modify sequence that does
not prune the key history. The plan is to add a new kadm5 protocol RPC
and convert clients to call that instead.
In setkey_principal_3 seal keys after entry key update
Also, for now, don't check the return value of kadm5_log_modify() in
the new kadm5_s_setkey_principal_3(). This has to be addressed more
globally.
Censor stale keys in kadm5_s_get_principal
We used to update the iprop log and HDB in different orders depending on
the kadm5 operation, which then led to various race conditions.
The iprop log now functions as a two-phase commit (with roll forward)
log for HDB changes. The log is auto-truncated, keeping the latest
entries that fit in a configurable maximum number of bytes (defaults to
50MB). See the log-max-size parameter description in krb5.conf(5).
The iprop log format and the protocol remain backwards-compatible with
earlier versions of Heimdal. This is NOT a flag-day; there is NO need
to update all the slaves at once with the master, though it is advisable
in general. Rolling upgrades and downgrades should work.
The sequence of updates is now (with HDB and log open and locked):
a) check that the HDB operation will succeed if attempted,
b) append to iprop log and fsync() it,
c) write to HDB (which should fsync()),
d) mark last log record committed (no fsync in this case).
Every kadm5 write operation recover transactions not yet confirmed as
committed, thus there can be at most one unconfirmed commit on a master
KDC.
Reads via kadm5_get_principal() also attempt to lock the log, and if
successful, recover unconfirmed transactions; readers must have write
access and must win any race to lock the iprop log.
The ipropd-master daemon also attempts to recover unconfirmed
transactions when idle.
The log now starts with a nop record whose payload records the offset of
the logical end of the log: the end of the last confirmed committed
transaction. This is kown as the "uber record". Its purpose is
two-fold: act as the confirmation of committed transactions, and provide
an O(1) method of finding the end of the log (i.e., without having to
traverse the entire log front to back).
Two-phase commit makes all kadm5 writes single-operation atomic
transactions (though some kadm5 operations, such as renames of
principals, and changes to principals' aliases, use multiple low-level
HDB write operations, but still all in one transaction). One can still
hold a lock on the HDB across many operations (e.g., by using the lock
command in a kadmin -l or calling kadm5_lock()) in order to push
multiple transactions in sequence, but this sequence will not be atomic
if the process or host crashes in the middle.
As before, HDB writes which do not go through the kadm5 API are excluded
from all of this, but there should be no such writes.
Lastly, the iprop-log(1) command is enhanced as follows:
- The dump, last-version, truncate, and replay sub-commands now have an
option to not lock the log. This is useful for inspecting a running
system's log file, especially on slave KDCs.
- The dump, last-version, truncate, and replay sub-commands now take an
optional iprop log file positional argument, so that they may be used
to inspect log files other than the running system's
configured/default log file.
Extensive code review and some re-writing for clarity by Viktor Dukhovni.
Included is a default plugin that searches a sorted text file where
every line is of the form:
<unparsed-principal>[<whitespace><username>]
If the username is missing in a matching line then an error is
returned. If a matching line is not found then the next plugin will
be allowed to run, if any.
Different ticket session key enctype selection options should
distinguish between target principal type (krbtgt vs. not), not
between KDC request types.