Files
heimdal/lib/asn1/kx509.asn1
Nicolas Williams 0729692cc8 asn1: Templates work for IMPLICIT; add build opt
Finally.  We're almost at parity for the template compiler.

Now we have a build option to use templating:

    `./configure --enable-asn1-templating`

Tests fail if you build `rfc2459.asn1` with `--template`.

TBD: Figure out what differences remain between the two compilers, and
     fix the templating compiler accordingly, adding tests along the
     way.

Making IMPLICIT tags work in the templating compiler turned out to be a
simple fix: don't attempt to do anything clever about IMPLICIT tags in
the template generator in the compiler other than denoting them --
instead leave all the smarts about IMPLICIT tags to the interpreter.
This might be a very slight pessimization, but also a great
simplification.

The result is very elegant: when the interpreter finds an IMPLICIT
tag it then recurses to find the template for the body of the type
so-tagged, and evaluates that.  Much more elegant than the code
generated by the non-template compiler, not least for not needing
any additional temporary memory allocation.

With this we finally have parity in basic testing of the template
compiler.  Indeed, for IMPLICIT tags the template compiler and
interpreter might even be better because they support IMPLICIT tags
with BER lengths, whereas the non-template compiler doesn't (mostly
because `der_replace_tag()` needs to be changed to support it.

And, of course, the template compiler is simply superior in that it
produces smaller code and is *much* easier to work with because the
functions to interpret templates are small and simple.  Which means we
can add more functions to deal with other encoding rules fairly
trivially.  It should be possible to add all of these with very little
work, almost all of it localized to `lib/asn1/template.c`:

 - PER  Packed Encoding Rules [X.691]
 - XER  XML Encoding Rules    [X.693]
 - OER  Octet Encoding Rules  [X.696] (intended to replace PER)
 - JER  JSON Encoding Rules   [X.697] (doubles as visual representation)
 - GSER Generic String E.R.s  [RFC3641] (a visual representation)

 - XDR  External Data Repr.   [STD67][RFC4506]

       (XDR is *not* an ASN.1 encoding rules specification, but it's a
        *lot* like PER/OER but with 4-octet alignment, and is specified
        for the syntax equivalent (XDR) of only a subset of ASN.1 syntax
        and semantics.)

All we'd have to do is add variants of `_asn1_{length,encode,decode}()`
for each set of rules, then generate per-type stub functions that call
them (as we already do for DER).

We could then have an encoding rule transliteration program that takes a
`TypeName` and some representation of a value encoded by some encoding
rules, and outputs the same thing encoded by a different set of rules.
This would double as a pretty-printer and parser if we do add support
for JER and/or GSER.  It would find the template for the given type
using `dlsym()` against some shared object (possibly `libasn1` itself).

Whereas generating source code for C (or whatever language) for
additional ERs requires much more work.  Plus, templates are much
smaller, and the interpreter is tiny, which yields much smaller text and
much smaller CPU icache/dcache footprint, which yields better
performance in many cases.

As well, the template system should be much easier to port to other
languages.  Though in the cases of, e.g., Rust, it would require use of
`unsafe` in the interpreter, so in fact the inverse might be true: that
it's easier to generate safe Rust code than to implement a template
interpreter in Rust.  Similarly for Haskell, OCAML, etc.  But wherever
the template interpreter is easy to implement, it's a huge win.

Note that implementing OER and PER using the templates as they are
currently would be a bit of a challenge, as the interpreter would have
to first do a pass of each SEQUENCE/SET to determine the size and
layout of the OER/PER sequence/set preamble by counting the number of
OPTIONAL/DEFAULT members, BOOLEAN members, and extensibility markers
with extensions present.  We could always generate more entries to
encode precomputed preamble metadata.  We would also need to add a
template entry type for extensibility markers, which currently we do
not.
2021-01-23 17:48:12 -06:00

205 lines
9.3 KiB
Groff

-- $Id$
-- Version 2 of the kx509 protocol is documented in RFC6717.
--
-- Our version here has extensions without changing the version number on the
-- wire.
KX509 DEFINITIONS ::= BEGIN
IMPORTS Extensions FROM rfc2459
KerberosTime FROM krb5;
KX509-ERROR-CODE ::= INTEGER {
KX509-STATUS-GOOD(0),
KX509-STATUS-CLIENT-BAD(1),
KX509-STATUS-CLIENT-FIX(2),
KX509-STATUS-CLIENT-TEMP(3),
KX509-STATUS-SERVER-BAD(4),
KX509-STATUS-SERVER-TEMP(5),
-- 6 is used internally in the umich client, avoid that
KX509-STATUS-SERVER-KEY(7),
-- CSR use negotiation:
KX509-STATUS-CLIENT-USE-CSR(8)
-- Let us reserve 1000+ for Kebreros protocol wire error codes -Nico
}
-- Originally kx509 requests carried only a public key. We'd like to have
-- proof of possession, and the ability to carry additional options, both, in
-- cleartext and otherwise.
--
-- We'll use a CSR for proof of posession and desired certificate extensions.
--
-- We'll also provide a non-CSR-based method of conveying desired certificate
-- extensions. The reason for this is simply that we may want to have a [e.g.,
-- RESTful HTTP] proxy for the kx509 service, and we want clients to be able to
-- be as simple as possible -cargo-culted even- with support for attributes
-- (desired certificate extensions) as parameters outside the CSR that the
-- proxy can encode without having the private key for the CSR (naturally).
--
-- I.e., ultimately we'll have a REST endpoint, /kx509, say, with query
-- parameters like:
--
-- - csr=<base64-encoding-of-DER-encoded-CSR>
-- - eku=<OID>
-- - ku=<key-usage-flag-name>
-- - rfc822Name=<URL-escaped-email-address>
-- - xMPPName=<URL-escaped-jabber-address>
-- - dNSName=<URL-escaped-FQDN>
-- - dNSSrv=<URL-escaped-_service.FQDN>
-- - registeredID=<OID>
-- - principalName=<URL-escaped-RFC1964-format-Kerberos-Principal-Name>
--
-- with exactly one CSR and zero, one, or more of the other parameters.
--
-- We'll even have a way to convey a bearer token from the REST proxy so that
-- we may have a way to get PKIX credentials using bearer tokens. And then,
-- using PKINIT, we may have a way to get Kerberos credentials using bearer
-- tokens.
--
-- To do this we define a Kx509CSRPlus that we can use in the `pk-key' field of
-- Kx509Request (see below):
Kx509CSRPlus ::= [APPLICATION 35] SEQUENCE {
-- PKCS#10, DER-encoded CSR, with or without meaningful attributes
csr OCTET STRING,
-- Desired certificate Extensions such as KeyUsage, ExtKeyUsage, or
-- subjectAlternativeName (SAN)
exts Extensions OPTIONAL,
-- Desired certificate lifetime
req-life KerberosTime OPTIONAL,
...
}
-- Version 2
Kx509Request ::= SEQUENCE {
authenticator OCTET STRING,
pk-hash OCTET STRING, -- HMAC(ticket_session_key, pk-key)
pk-key OCTET STRING -- one of:
-- - the public key, DER-encoded (RSA, basically)
-- - a Kx509CSRPlus
}
-- 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] Kx509ErrorCode DEFAULT 0,
hash[1] OCTET STRING OPTIONAL, -- HMAC(session_key, ...)
certificate[2] OCTET STRING OPTIONAL, -- DER-encoded Certificate
-- if client sent raw RSA SPK
-- or DER-encoded Certificates
-- (i.e., SEQ. OF Certificate)
-- 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