diff --git a/README.fast b/README.fast new file mode 100644 index 000000000..389b6af47 --- /dev/null +++ b/README.fast @@ -0,0 +1,17 @@ + +-- in order of preference + +- client: support KRB5_PADATA_ENCRYPTED_CHALLENGE in lib/krb5/init_creds_pw.c +- client: don't support ENC-TS in FAST + +- client: plugin support for fast plugins + +- kdc: plugin support for fast plugins + partly done with "struct kdc_patypes" + +- kcm: support FAST armor ticket +-- using PK-INIT anonymous +-- using host key + +- client: tgs-req fast support +- kdc: tgs-req fast support diff --git a/doc/standardisation/rfc6112.txt b/doc/standardisation/rfc6112.txt new file mode 100644 index 000000000..b40775966 --- /dev/null +++ b/doc/standardisation/rfc6112.txt @@ -0,0 +1,899 @@ + + + + + + +Internet Engineering Task Force (IETF) L. Zhu +Request for Comments: 6112 P. Leach +Updates: 4120, 4121, 4556 Microsoft Corporation +Category: Standards Track S. Hartman +ISSN: 2070-1721 Painless Security + April 2011 + + + Anonymity Support for Kerberos + +Abstract + + This document defines extensions to the Kerberos protocol to allow a + Kerberos client to securely communicate with a Kerberos application + service without revealing its identity, or without revealing more + than its Kerberos realm. It also defines extensions that allow a + Kerberos client to obtain anonymous credentials without revealing its + identity to the Kerberos Key Distribution Center (KDC). This + document updates RFCs 4120, 4121, and 4556. + +Status of This Memo + + This is an Internet Standards Track document. + + This document is a product of the Internet Engineering Task Force + (IETF). It represents the consensus of the IETF community. It has + received public review and has been approved for publication by the + Internet Engineering Steering Group (IESG). Further information on + Internet Standards is available in Section 2 of RFC 5741. + + Information about the current status of this document, any errata, + and how to provide feedback on it may be obtained at + http://www.rfc-editor.org/info/rfc6112. + +Copyright Notice + + Copyright (c) 2011 IETF Trust and the persons identified as the + document authors. All rights reserved. + + This document is subject to BCP 78 and the IETF Trust's Legal + Provisions Relating to IETF Documents + (http://trustee.ietf.org/license-info) in effect on the date of + publication of this document. Please review these documents + carefully, as they describe your rights and restrictions with respect + to this document. Code Components extracted from this document must + include Simplified BSD License text as described in Section 4.e of + the Trust Legal Provisions and are provided without warranty as + described in the Simplified BSD License. + + + +Zhu, et al. Standards Track [Page 1] + +RFC 6112 Kerberos Anonymity Support April 2011 + + + This document may contain material from IETF Documents or IETF + Contributions published or made publicly available before November + 10, 2008. The person(s) controlling the copyright in some of this + material may not have granted the IETF Trust the right to allow + modifications of such material outside the IETF Standards Process. + Without obtaining an adequate license from the person(s) controlling + the copyright in such materials, this document may not be modified + outside the IETF Standards Process, and derivative works of it may + not be created outside the IETF Standards Process, except to format + it for publication as an RFC or to translate it into languages other + than English. + +Table of Contents + + 1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . . 3 + 2. Conventions Used in This Document . . . . . . . . . . . . . . 3 + 3. Definitions . . . . . . . . . . . . . . . . . . . . . . . . . 3 + 4. Protocol Description . . . . . . . . . . . . . . . . . . . . . 5 + 4.1. Anonymity Support in AS Exchange . . . . . . . . . . . . . 5 + 4.1.1. Anonymous PKINIT . . . . . . . . . . . . . . . . . . . 6 + 4.2. Anonymity Support in TGS Exchange . . . . . . . . . . . . 7 + 4.3. Subsequent Exchanges and Protocol Actions Common to AS + and TGS for Anonymity Support . . . . . . . . . . . . . . 9 + 5. Interoperability Requirements . . . . . . . . . . . . . . . . 10 + 6. GSS-API Implementation Notes . . . . . . . . . . . . . . . . . 10 + 7. PKINIT Client Contribution to the Ticket Session Key . . . . . 11 + 7.1. Combining Two Protocol Keys . . . . . . . . . . . . . . . 12 + 8. Security Considerations . . . . . . . . . . . . . . . . . . . 13 + 9. Acknowledgements . . . . . . . . . . . . . . . . . . . . . . . 14 + 10. IANA Considerations . . . . . . . . . . . . . . . . . . . . . 15 + 11. References . . . . . . . . . . . . . . . . . . . . . . . . . . 15 + 11.1. Normative References . . . . . . . . . . . . . . . . . . . 15 + 11.2. Informative References . . . . . . . . . . . . . . . . . . 16 + + + + + + + + + + + + + + + + + + +Zhu, et al. Standards Track [Page 2] + +RFC 6112 Kerberos Anonymity Support April 2011 + + +1. Introduction + + In certain situations, the Kerberos [RFC4120] client may wish to + authenticate a server and/or protect communications without revealing + the client's own identity. For example, consider an application that + provides read access to a research database and that permits queries + by arbitrary requesters. A client of such a service might wish to + authenticate the service, to establish trust in the information + received from it, but might not wish to disclose the client's + identity to the service for privacy reasons. + + Extensions to Kerberos are specified in this document by which a + client can authenticate the Key Distribution Center (KDC) and request + an anonymous ticket. The client can use the anonymous ticket to + authenticate the server and protect subsequent client-server + communications. + + By using the extensions defined in this specification, the client can + request an anonymous ticket where the client may reveal the client's + identity to the client's own KDC, or the client can hide the client's + identity completely by using anonymous Public Key Cryptography for + Initial Authentication in Kerberos (PKINIT) as defined in + Section 4.1. Using the returned anonymous ticket, the client remains + anonymous in subsequent Kerberos exchanges thereafter to KDCs on the + cross-realm authentication path and to the server with which it + communicates. + + In this specification, the client realm in the anonymous ticket is + the anonymous realm name when anonymous PKINIT is used to obtain the + ticket. The client realm is the client's real realm name if the + client is authenticated using the client's long-term keys. Note that + the membership of a realm can imply a member of the community + represented by the realm. + + The interaction with Generic Security Service Application Program + Interface (GSS-API) is described after the protocol description. + +2. Conventions Used in This Document + + The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", + "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this + document are to be interpreted as described in [RFC2119]. + +3. Definitions + + The anonymous Kerberos realm name is defined as a well-known realm + name based on [RFC6111], and the value of this well-known realm name + is the literal "WELLKNOWN:ANONYMOUS". + + + +Zhu, et al. Standards Track [Page 3] + +RFC 6112 Kerberos Anonymity Support April 2011 + + + The anonymous Kerberos principal name is defined as a well-known + Kerberos principal name based on [RFC6111]. The value of the name- + type field is KRB_NT_WELLKNOWN [RFC6111], and the value of the name- + string field is a sequence of two KerberosString components: + "WELLKNOWN", "ANONYMOUS". + + The anonymous ticket flag is defined as bit 16 (with the first bit + being bit 0) in the TicketFlags: + + TicketFlags ::= KerberosFlags + -- anonymous(16) + -- TicketFlags and KerberosFlags are defined in [RFC4120] + + This is a new ticket flag that is used to indicate that a ticket is + an anonymous one. + + An anonymous ticket is a ticket that has all of the following + properties: + + o The cname field contains the anonymous Kerberos principal name. + + o The crealm field contains the client's realm name or the anonymous + realm name. + + o The anonymous ticket contains no information that can reveal the + client's identity. However, the ticket may contain the client + realm, intermediate realms on the client's authentication path, + and authorization data that may provide information related to the + client's identity. For example, an anonymous principal that is + identifiable only within a particular group of users can be + implemented using authorization data and such authorization data, + if included in the anonymous ticket, would disclose the client's + membership of that group. + + o The anonymous ticket flag is set. + + The anonymous KDC option is defined as bit 16 (with the first bit + being bit 0) in the KDCOptions: + + KDCOptions ::= KerberosFlags + -- anonymous(16) + -- KDCOptions and KerberosFlags are defined in [RFC4120] + + + + + + + + + +Zhu, et al. Standards Track [Page 4] + +RFC 6112 Kerberos Anonymity Support April 2011 + + + As described in Section 4, the anonymous KDC option is set to request + an anonymous ticket in an Authentication Service (AS) request or a + Ticket Granting Service (TGS) request. + +4. Protocol Description + + In order to request an anonymous ticket, the client sets the + anonymous KDC option in an AS request or a TGS request. + + The rest of this section is organized as follows: it first describes + protocol actions specific to AS exchanges, then it describes those of + TGS exchanges. These are then followed by the description of + protocol actions common to both AS and TGS and those in subsequent + exchanges. + +4.1. Anonymity Support in AS Exchange + + The client requests an anonymous ticket by setting the anonymous KDC + option in an AS exchange. + + The Kerberos client can use the client's long-term keys, the client's + X.509 certificates [RFC4556], or any other pre-authentication data, + to authenticate to the KDC and requests an anonymous ticket in an AS + exchange where the client's identity is known to the KDC. + + If the client in the AS request is anonymous, the anonymous KDC + option MUST be set in the request. Otherwise, the KDC MUST return a + KRB-ERROR message with the code KDC_ERR_BADOPTION. + + If the client is anonymous and the KDC does not have a key to encrypt + the reply (this can happen when, for example, the KDC does not + support PKINIT [RFC4556]), the KDC MUST return an error message with + the code KDC_ERR_NULL_KEY [RFC4120]. + + When policy allows, the KDC issues an anonymous ticket. If the + client name in the request is the anonymous principal, the client + realm (crealm) in the reply is the anonymous realm, otherwise, the + client realm is the realm of the AS. According to [RFC4120], the + client name and the client realm in the EncTicketPart of the reply + MUST match with the corresponding client name and the client realm of + the KDC reply; the client MUST use the client name and the client + realm returned in the KDC-REP in subsequent message exchanges when + using the obtained anonymous ticket. + + Care MUST be taken by the KDC not to reveal the client's identity in + the authorization data of the returned ticket when populating the + authorization data in a returned anonymous ticket. + + + + +Zhu, et al. Standards Track [Page 5] + +RFC 6112 Kerberos Anonymity Support April 2011 + + + The AD-INITIAL-VERIFIED-CAS authorization data, as defined in + [RFC4556], contains the issuer name of the client certificate. This + authorization is not applicable and MUST NOT be present in the + returned anonymous ticket when anonymous PKINIT is used. When the + client is authenticated (i.e., anonymous PKINIT is not used), if it + is undesirable to disclose such information about the client's + identity, the AD-INITIAL-VERIFIED-CAS authorization data SHOULD be + removed from the returned anonymous ticket. + + The client can use the client keys to mutually authenticate with the + KDC and request an anonymous Ticket Granting Ticket (TGT) in the AS + request. In that case, the reply key is selected as normal, + according to Section 3.1.3 of [RFC4120]. + +4.1.1. Anonymous PKINIT + + This sub-section defines anonymous PKINIT. + + As described earlier in this section, the client can request an + anonymous ticket by authenticating to the KDC using the client's + identity; alternatively, without revealing the client's identity to + the KDC, the Kerberos client can request an anonymous ticket as + follows: the client sets the client name as the anonymous principal + in the AS exchange and provides PA_PK_AS_REQ pre-authentication data + [RFC4556] where the signerInfos field of the SignedData [RFC5652] of + the PA_PK_AS_REQ is empty, and the certificates field is absent. + Because the anonymous client does not have an associated asymmetric + key pair, the client MUST choose the Diffie-Hellman key agreement + method by filling in the Diffie-Hellman domain parameters in the + clientPublicValue [RFC4556]. This use of the anonymous client name + in conjunction with PKINIT is referred to as anonymous PKINIT. If + anonymous PKINIT is used, the realm name in the returned anonymous + ticket MUST be the anonymous realm. + + Upon receiving the anonymous PKINIT request from the client, the KDC + processes the request, according to Section 3.1.2 of [RFC4120]. The + KDC skips the checks for the client's signature and the client's + public key (such as the verification of the binding between the + client's public key and the client name), but performs otherwise + applicable checks, and proceeds as normal, according to [RFC4556]. + For example, the AS MUST check if the client's Diffie-Hellman domain + parameters are acceptable. The Diffie-Hellman key agreement method + MUST be used and the reply key is derived according to Section + 3.2.3.1 of [RFC4556]. If the clientPublicValue is not present in the + request, the KDC MUST return a KRB-ERROR with the code + KDC_ERR_PUBLIC_KEY_ENCRYPTION_NOT_SUPPORTED [RFC4556]. If all goes + well, an anonymous ticket is generated, according to Section 3.1.3 of + [RFC4120], and PA_PK_AS_REP [RFC4556] pre-authentication data is + + + +Zhu, et al. Standards Track [Page 6] + +RFC 6112 Kerberos Anonymity Support April 2011 + + + included in the KDC reply, according to [RFC4556]. If the KDC does + not have an asymmetric key pair, it MAY reply anonymously or reject + the authentication attempt. If the KDC replies anonymously, the + signerInfos field of the SignedData [RFC5652] of PA_PK_AS_REP in the + reply is empty, and the certificates field is absent. The server + name in the anonymous KDC reply contains the name of the TGS. + + Upon receipt of the KDC reply that contains an anonymous ticket and + PA_PK_AS_REP [RFC4556] pre-authentication data, the client can then + authenticate the KDC based on the KDC's signature in the + PA_PK_AS_REP. If the KDC's signature is missing in the KDC reply + (the reply is anonymous), the client MUST reject the returned ticket + if it cannot authenticate the KDC otherwise. + + A KDC that supports anonymous PKINIT MUST indicate the support of + PKINIT, according to Section 3.4 of [RFC4556]. In addition, such a + KDC MUST indicate support for anonymous PKINIT by including a padata + element of padata-type PA_PKINIT_KX and empty padata-value when + including PA-PK-AS-REQ in an error reply. + + When included in a KDC error, PA_PKINIT_KX indicates support for + anonymous PKINIT. As discussed in Section 7, when included in an AS- + REP, PA_PKINIT_KX proves that the KDC and client both contributed to + the session key for any use of Diffie-Hellman key agreement with + PKINIT. + + Note that in order to obtain an anonymous ticket with the anonymous + realm name, the client MUST set the client name as the anonymous + principal in the request when requesting an anonymous ticket in an AS + exchange. Anonymity PKINIT is the only way via which an anonymous + ticket with the anonymous realm as the client realm can be generated + in this specification. + +4.2. Anonymity Support in TGS Exchange + + The client requests an anonymous ticket by setting the anonymous KDC + option in a TGS exchange, and in that request the client can use a + normal Ticket Granting Ticket (TGT) with the client's identity, or an + anonymous TGT, or an anonymous cross-realm TGT. If the client uses a + normal TGT, the client's identity is known to the TGS. + + Note that the client can completely hide the client's identity in an + AS exchange using anonymous PKINIT, as described in the previous + section. + + + + + + + +Zhu, et al. Standards Track [Page 7] + +RFC 6112 Kerberos Anonymity Support April 2011 + + + If the ticket in the PA-TGS-REQ of the TGS request is an anonymous + one, the anonymous KDC option MUST be set in the request. Otherwise, + the KDC MUST return a KRB-ERROR message with the code + KDC_ERR_BADOPTION. + + When policy allows, the KDC issues an anonymous ticket. If the + ticket in the TGS request is an anonymous one, the client name and + the client realm are copied from that ticket; otherwise, the ticket + in the TGS request is a normal ticket, the returned anonymous ticket + contains the client name as the anonymous principal and the client + realm as the true realm of the client. In all cases, according to + [RFC4120] the client name and the client realm in the EncTicketPart + of the reply MUST match with the corresponding client name and the + client realm of the anonymous ticket in the reply; the client MUST + use the client name and the client realm returned in the KDC-REP in + subsequent message exchanges when using the obtained anonymous + ticket. + + Care MUST be taken by the TGS not to reveal the client's identity in + the authorization data of the returned ticket. When propagating + authorization data in the ticket or in the enc-authorization-data + field of the request, the TGS MUST ensure that the client + confidentiality is not violated in the returned anonymous ticket. + The TGS MUST process the authorization data recursively, according to + Section 5.2.6 of [RFC4120], beyond the container levels such that all + embedded authorization elements are interpreted. The TGS SHOULD NOT + populate identity-based authorization data into an anonymous ticket + in that such authorization data typically reveals the client's + identity. The specification of a new authorization data type MUST + specify the processing rules of the authorization data when an + anonymous ticket is returned. If there is no processing rule defined + for an authorization data element or the authorization data element + is unknown, the TGS MUST process it when an anonymous ticket is + returned as follows: + + o If the authorization data element may reveal the client's + identity, it MUST be removed unless otherwise specified. + + o If the authorization data element, that could reveal the client's + identity, is intended to restrict the use of the ticket or limit + the rights otherwise conveyed in the ticket, it cannot be removed + in order to hide the client's identity. In this case, the + authentication attempt MUST be rejected, and the TGS MUST return + an error message with the code KDC_ERR_POLICY. Note this is + applicable to both critical and optional authorization data. + + + + + + +Zhu, et al. Standards Track [Page 8] + +RFC 6112 Kerberos Anonymity Support April 2011 + + + o If the authorization data element is unknown, the TGS MAY remove + it, or transfer it into the returned anonymous ticket, or reject + the authentication attempt, based on local policy for that + authorization data type unless otherwise specified. If there is + no policy defined for a given unknown authorization data type, the + authentication MUST be rejected. The error code is KDC_ERR_POLICY + when the authentication is rejected. + + The AD-INITIAL-VERIFIED-CAS authorization data, as defined in + [RFC4556], contains the issuer name of the client certificate. If it + is undesirable to disclose such information about the client's + identity, the AD-INITIAL-VERIFIED-CAS authorization data SHOULD be + removed from an anonymous ticket. + + The TGS encodes the name of the previous realm into the transited + field, according to Section 3.3.3.2 of [RFC4120]. Based on local + policy, the TGS MAY omit the previous realm, if the cross realm TGT + is an anonymous one, in order to hide the authentication path of the + client. The unordered set of realms in the transited field, if + present, can reveal which realm may potentially be the realm of the + client or the realm that issued the anonymous TGT. The anonymous + Kerberos realm name MUST NOT be present in the transited field of a + ticket. The true name of the realm that issued the anonymous ticket + MAY be present in the transited field of a ticket. + +4.3. Subsequent Exchanges and Protocol Actions Common to AS and TGS for + Anonymity Support + + In both AS and TGS exchanges, the realm field in the KDC request is + always the realm of the target KDC, not the anonymous realm when the + client requests an anonymous ticket. + + Absent other information, the KDC MUST NOT include any identifier in + the returned anonymous ticket that could reveal the client's identity + to the server. + + Unless anonymous PKINIT is used, if a client requires anonymous + communication, then the client MUST check to make sure that the + ticket in the reply is actually anonymous by checking the presence of + the anonymous ticket flag in the flags field of the EncKDCRepPart. + This is because KDCs ignore unknown KDC options. A KDC that does not + understand the anonymous KDC option will not return an error, but + will instead return a normal ticket. + + The subsequent client and server communications then proceed as + described in [RFC4120]. + + + + + +Zhu, et al. Standards Track [Page 9] + +RFC 6112 Kerberos Anonymity Support April 2011 + + + Note that the anonymous principal name and realm are only applicable + to the client in Kerberos messages, the server cannot be anonymous in + any Kerberos message per this specification. + + A server accepting an anonymous service ticket may assume that + subsequent requests using the same ticket originate from the same + client. Requests with different tickets are likely to originate from + different clients. + + Upon receipt of an anonymous ticket, the transited policy check is + performed in the same way as that of a normal ticket if the client's + realm is not the anonymous realm; if the client realm is the + anonymous realm, absent other information any realm in the + authentication path is allowed by the cross-realm policy check. + +5. Interoperability Requirements + + Conforming implementations MUST support the anonymous principal with + a non-anonymous realm, and they MAY support the anonymous principal + with the anonymous realm using anonymous PKINIT. + +6. GSS-API Implementation Notes + + GSS-API defines the name_type GSS_C_NT_ANONYMOUS [RFC2743] to + represent the anonymous identity. In addition, Section 2.1.1 of + [RFC1964] defines the single string representation of a Kerberos + principal name with the name_type GSS_KRB5_NT_PRINCIPAL_NAME. The + anonymous principal with the anonymous realm corresponds to the GSS- + API anonymous principal. A principal with the anonymous principal + name and a non-anonymous realm is an authenticated principal; hence, + such a principal does not correspond to the anonymous principal in + GSS-API with the GSS_C_NT_ANONYMOUS name type. The [RFC1964] name + syntax for GSS_KRB5_NT_PRINCIPAL_NAME MUST be used for importing the + anonymous principal name with a non-anonymous realm name and for + displaying and exporting these names. In addition, this syntax must + be used along with the name type GSS_C_NT_ANONYMOUS for displaying + and exporting the anonymous principal with the anonymous realm. + + At the GSS-API [RFC2743] level, an initiator/client requests the use + of an anonymous principal with the anonymous realm by asserting the + "anonymous" flag when calling GSS_Init_Sec_Context(). The GSS-API + implementation MAY provide implementation-specific means for + requesting the use of an anonymous principal with a non-anonymous + realm. + + GSS-API does not know or define "anonymous credentials", so the + (printable) name of the anonymous principal will rarely be used by or + relevant for the initiator/client. The printable name is relevant + + + +Zhu, et al. Standards Track [Page 10] + +RFC 6112 Kerberos Anonymity Support April 2011 + + + for the acceptor/server when performing an authorization decision + based on the initiator name that is returned from the acceptor side + upon the successful security context establishment. + + A GSS-API initiator MUST carefully check the resulting context + attributes from the initial call to GSS_Init_Sec_Context() when + requesting anonymity, because (as in the GSS-API tradition and for + backwards compatibility) anonymity is just another optional context + attribute. It could be that the mechanism doesn't recognize the + attribute at all or that anonymity is not available for some other + reasons -- and in that case the initiator MUST NOT send the initial + security context token to the acceptor, because it will likely reveal + the initiators identity to the acceptor, something that can rarely be + "un-done". + + Portable initiators are RECOMMENDED to use default credentials + whenever possible, and request anonymity only through the input + anon_req_flag [RFC2743] to GSS_Init_Sec_Context(). + +7. PKINIT Client Contribution to the Ticket Session Key + + The definition in this section was motivated by protocol analysis of + anonymous PKINIT (defined in this document) in building tunneling + channels [RFC6113] and subsequent channel bindings. In order to + enable applications of anonymous PKINIT to form channels, all + implementations of anonymous PKINIT need to meet the requirements of + this section. There is otherwise no connection to the rest of this + document. + + PKINIT is useful for constructing tunneling channels. To ensure that + an attacker cannot create a channel with a given name, it is + desirable that neither the KDC nor the client unilaterally determine + the ticket session key. To achieve that end, a KDC conforming to + this definition MUST encrypt a randomly generated key, called the KDC + contribution key, in the PA_PKINIT_KX padata (defined next in this + section). The KDC contribution key is then combined with the reply + key to form the ticket session key of the returned ticket. These two + keys are then combined using the KRB-FX-CF2 operation defined in + Section 7.1, where K1 is the KDC contribution key, K2 is the reply + key, the input pepper1 is American Standard Code for Information + Interchange (ASCII) [ASAX34] string "PKINIT", and the input pepper2 + is ASCII string "KeyExchange". + + + + + + + + + +Zhu, et al. Standards Track [Page 11] + +RFC 6112 Kerberos Anonymity Support April 2011 + + + PA_PKINIT_KX 147 + -- padata for PKINIT that contains an encrypted + -- KDC contribution key. + + PA-PKINIT-KX ::= EncryptedData -- EncryptionKey + -- Contains an encrypted key randomly + -- generated by the KDC (known as the KDC contribution key). + -- Both EncryptedData and EncryptionKey are defined in [RFC4120] + + The PA_PKINIT_KX padata MUST be included in the KDC reply when + anonymous PKINIT is used; it SHOULD be included if PKINIT is used + with the Diffie-Hellman key exchange but the client is not anonymous; + it MUST NOT be included otherwise (e.g., when PKINIT is used with the + public key encryption as the key exchange). + + The padata-value field of the PA-PKINIT-KX type padata contains the + DER [X.680] [X.690] encoding of the Abstract Syntax Notation One + (ASN.1) type PA-PKINIT-KX. The PA-PKINIT-KX structure is an + EncryptedData. The cleartext data being encrypted is the DER-encoded + KDC contribution key randomly generated by the KDC. The encryption + key is the reply key and the key usage number is + KEY_USAGE_PA_PKINIT_KX (44). + + The client then decrypts the KDC contribution key and verifies the + ticket session key in the returned ticket is the combined key of the + KDC contribution key and the reply key as described above. A + conforming client MUST reject anonymous PKINIT authentication if the + PA_PKINIT_KX padata is not present in the KDC reply or if the ticket + session key of the returned ticket is not the combined key of the KDC + contribution key and the reply key when PA-PKINIT-KX is present in + the KDC reply. + +7.1. Combining Two Protocol Keys + + KRB-FX-CF2() combines two protocol keys based on the pseudo-random() + function defined in [RFC3961]. + + Given two input keys, K1 and K2, where K1 and K2 can be of two + different enctypes, the output key of KRB-FX-CF2(), K3, is derived as + follows: + + KRB-FX-CF2(protocol key, protocol key, octet string, + octet string) -> (protocol key) + + PRF+(K1, pepper1) -> octet-string-1 + PRF+(K2, pepper2) -> octet-string-2 + KRB-FX-CF2(K1, K2, pepper1, pepper2) -> + random-to-key(octet-string-1 ^ octet-string-2) + + + +Zhu, et al. Standards Track [Page 12] + +RFC 6112 Kerberos Anonymity Support April 2011 + + + Where ^ denotes the exclusive-OR operation. PRF+() is defined as + follows: + + PRF+(protocol key, octet string) -> (octet string) + + PRF+(key, shared-info) -> pseudo-random( key, 1 || shared-info ) || + pseudo-random( key, 2 || shared-info ) || + pseudo-random( key, 3 || shared-info ) || ... + + Here the counter value 1, 2, 3, and so on are encoded as a one-octet + integer. The pseudo-random() operation is specified by the enctype + of the protocol key. PRF+() uses the counter to generate enough bits + as needed by the random-to-key() [RFC3961] function for the + encryption type specified for the resulting key; unneeded bits are + removed from the tail. + +8. Security Considerations + + Since KDCs ignore unknown options, a client requiring anonymous + communication needs to make sure that the returned ticket is actually + anonymous. This is because a KDC that does not understand the + anonymous option would not return an anonymous ticket. + + By using the mechanism defined in this specification, the client does + not reveal the client's identity to the server but the client + identity may be revealed to the KDC of the server principal (when the + server principal is in a different realm than that of the client), + and any KDC on the cross-realm authentication path. The Kerberos + client MUST verify the ticket being used is indeed anonymous before + communicating with the server, otherwise, the client's identity may + be revealed unintentionally. + + In cases where specific server principals must not have access to the + client's identity (for example, an anonymous poll service), the KDC + can define server-principal-specific policy that ensures any normal + service ticket can NEVER be issued to any of these server principals. + + If the KDC that issued an anonymous ticket were to maintain records + of the association of identities to an anonymous ticket, then someone + obtaining such records could breach the anonymity. Additionally, the + implementations of most (for now all) KDC's respond to requests at + the time that they are received. Traffic analysis on the connection + to the KDC will allow an attacker to match client identities to + anonymous tickets issued. Because there are plaintext parts of the + tickets that are exposed on the wire, such matching by a third-party + observer is relatively straightforward. A service that is + authenticated by the anonymous principals may be able to infer the + + + + +Zhu, et al. Standards Track [Page 13] + +RFC 6112 Kerberos Anonymity Support April 2011 + + + identity of the client by examining and linking quasi-static protocol + information such as the IP address from which a request is received, + or by linking multiple uses of the same anonymous ticket. + + Two mechanisms, the FAST facility with the hide-client-names option + in [RFC6113] and the Kerberos5 starttls option [STARTTLS], protect + the client identity so that an attacker would never be able to + observe the client identity sent to the KDC. Transport or network + layer security between the client and the server will help prevent + tracking of a particular ticket to link a ticket to a user. In + addition, clients can limit how often a ticket is reused to minimize + ticket linking. + + The client's real identity is not revealed when the client is + authenticated as the anonymous principal. Application servers MAY + reject the authentication in order to, for example, prevent + information disclosure or as part of Denial of Service (DoS) + prevention. Application servers MUST avoid accepting anonymous + credentials in situations where they must record the client's + identity; for example, when there must be an audit trail. + +9. Acknowledgements + + JK Jaganathan helped editing early revisions of this document. + + Clifford Neuman contributed the core notions of this document. + + Ken Raeburn reviewed the document and provided suggestions for + improvements. + + Martin Rex wrote the text for GSS-API considerations. + + Nicolas Williams reviewed the GSS-API considerations section and + suggested ideas for improvements. + + Sam Hartman and Nicolas Williams were great champions of this work. + + Miguel Garcia and Phillip Hallam-Baker reviewed the document and + provided helpful suggestions. + + In addition, the following individuals made significant + contributions: Jeffrey Altman, Tom Yu, Chaskiel M Grundman, Love + Hornquist Astrand, Jeffrey Hutzelman, and Olga Kornievskaia. + + + + + + + + +Zhu, et al. Standards Track [Page 14] + +RFC 6112 Kerberos Anonymity Support April 2011 + + +10. IANA Considerations + + This document defines a new 'anonymous' Kerberos well-known name and + a new 'anonymous' Kerberos well-known realm based on [RFC6111]. IANA + has added these two values to the Kerberos naming registries that are + created in [RFC6111]. + +11. References + +11.1. Normative References + + [ASAX34] American Standards Institute, "American Standard Code for + Information Interchange", ASA X3.4-1963, June 1963. + + [RFC1964] Linn, J., "The Kerberos Version 5 GSS-API Mechanism", + RFC 1964, June 1996. + + [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate + Requirement Levels", BCP 14, RFC 2119, March 1997. + + [RFC2743] Linn, J., "Generic Security Service Application Program + Interface Version 2, Update 1", RFC 2743, January 2000. + + [RFC3961] Raeburn, K., "Encryption and Checksum Specifications for + Kerberos 5", RFC 3961, February 2005. + + [RFC4120] Neuman, C., Yu, T., Hartman, S., and K. Raeburn, "The + Kerberos Network Authentication Service (V5)", RFC 4120, + July 2005. + + [RFC4556] Zhu, L. and B. Tung, "Public Key Cryptography for Initial + Authentication in Kerberos (PKINIT)", RFC 4556, + June 2006. + + [RFC5652] Housley, R., "Cryptographic Message Syntax (CMS)", + STD 70, RFC 5652, September 2009. + + [RFC6111] Zhu, L., "Additional Kerberos Naming Constraints", + RFC 6111, April 2011. + + [X.680] "Abstract Syntax Notation One (ASN.1): Specification of + Basic Notation", ITU-T Recommendation X.680: ISO/IEC + International Standard 8824-1:1998, 1997. + + [X.690] "ASN.1 encoding rules: Specification of Basic Encoding + Rules (BER), Canonical Encoding Rules (CER) and + Distinguished Encoding Rules (DER)", ITU-T Recommendation + X.690 ISO/IEC International Standard 8825-1:1998, 1997. + + + +Zhu, et al. Standards Track [Page 15] + +RFC 6112 Kerberos Anonymity Support April 2011 + + +11.2. Informative References + + [RFC6113] Hartman, S. and L. Zhu, "A Generalized Framework for + Kerberos Pre-Authentication", RFC 6113, April 2011. + + [STARTTLS] Josefsson, S., "Using Kerberos V5 over the Transport + Layer Security (TLS) protocol", Work in Progress, + August 2010. + +Authors' Addresses + + Larry Zhu + Microsoft Corporation + One Microsoft Way + Redmond, WA 98052 + US + + EMail: larry.zhu@microsoft.com + + + Paul Leach + Microsoft Corporation + One Microsoft Way + Redmond, WA 98052 + US + + EMail: paulle@microsoft.com + + + Sam Hartman + Painless Security + + EMail: hartmans-ietf@mit.edu + + + + + + + + + + + + + + + + + + +Zhu, et al. Standards Track [Page 16] + diff --git a/doc/standardisation/rfc6113.txt b/doc/standardisation/rfc6113.txt new file mode 100644 index 000000000..e0a579eab --- /dev/null +++ b/doc/standardisation/rfc6113.txt @@ -0,0 +1,2691 @@ + + + + + + +Internet Engineering Task Force (IETF) S. Hartman +Request for Comments: 6113 Painless Security +Updates: 4120 L. Zhu +Category: Standards Track Microsoft Corporation +ISSN: 2070-1721 April 2011 + + + A Generalized Framework for Kerberos Pre-Authentication + +Abstract + + Kerberos is a protocol for verifying the identity of principals + (e.g., a workstation user or a network server) on an open network. + The Kerberos protocol provides a facility called pre-authentication. + Pre-authentication mechanisms can use this facility to extend the + Kerberos protocol and prove the identity of a principal. + + This document describes a more formal model for this facility. The + model describes what state in the Kerberos request a pre- + authentication mechanism is likely to change. It also describes how + multiple pre-authentication mechanisms used in the same request will + interact. + + This document also provides common tools needed by multiple pre- + authentication mechanisms. One of these tools is a secure channel + between the client and the key distribution center with a reply key + strengthening mechanism; this secure channel can be used to protect + the authentication exchange and thus eliminate offline dictionary + attacks. With these tools, it is relatively straightforward to chain + multiple authentication mechanisms, utilize a different key + management system, or support a new key agreement algorithm. + +Status of This Memo + + This is an Internet Standards Track document. + + This document is a product of the Internet Engineering Task Force + (IETF). It represents the consensus of the IETF community. It has + received public review and has been approved for publication by the + Internet Engineering Steering Group (IESG). Further information on + Internet Standards is available in Section 2 of RFC 5741. + + Information about the current status of this document, any errata, + and how to provide feedback on it may be obtained at + http://www.rfc-editor.org/info/rfc6113. + + + + + + +Hartman & Zhu Standards Track [Page 1] + +RFC 6113 Kerberos Preauth Framework April 2011 + + +Copyright Notice + + Copyright (c) 2011 IETF Trust and the persons identified as the + document authors. All rights reserved. + + This document is subject to BCP 78 and the IETF Trust's Legal + Provisions Relating to IETF Documents + (http://trustee.ietf.org/license-info) in effect on the date of + publication of this document. Please review these documents + carefully, as they describe your rights and restrictions with respect + to this document. Code Components extracted from this document must + include Simplified BSD License text as described in Section 4.e of + the Trust Legal Provisions and are provided without warranty as + described in the Simplified BSD License. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Hartman & Zhu Standards Track [Page 2] + +RFC 6113 Kerberos Preauth Framework April 2011 + + +Table of Contents + + 1. Introduction ....................................................4 + 1.1. Conventions and Terminology Used in This Document ..........5 + 1.2. Conformance Requirements ...................................5 + 2. Model for Pre-Authentication ....................................6 + 2.1. Information Managed by the Pre-Authentication Model ........7 + 2.2. Initial Pre-Authentication Required Error ..................9 + 2.3. Client to KDC .............................................10 + 2.4. KDC to Client .............................................11 + 3. Pre-Authentication Facilities ..................................12 + 3.1. Client Authentication Facility ............................13 + 3.2. Strengthening Reply Key Facility ..........................13 + 3.3. Replace Reply Key Facility ................................14 + 3.4. KDC Authentication Facility ...............................15 + 4. Requirements for Pre-Authentication Mechanisms .................15 + 4.1. Protecting Requests/Responses .............................16 + 5. Tools for Use in Pre-Authentication Mechanisms .................17 + 5.1. Combining Keys ............................................17 + 5.2. Managing States for the KDC ...............................19 + 5.3. Pre-Authentication Set ....................................20 + 5.4. Definition of Kerberos FAST Padata ........................23 + 5.4.1. FAST Armors ........................................24 + 5.4.2. FAST Request .......................................26 + 5.4.3. FAST Response ......................................30 + 5.4.4. Authenticated Kerberos Error Messages Using + Kerberos FAST ......................................33 + 5.4.5. Outer and Inner Requests ...........................34 + 5.4.6. The Encrypted Challenge FAST Factor ................34 + 5.5. Authentication Strength Indication ........................36 + 6. Assigned Constants .............................................37 + 6.1. New Errors ................................................37 + 6.2. Key Usage Numbers .........................................37 + 6.3. Authorization Data Elements ...............................37 + 6.4. New PA-DATA Types .........................................37 + 7. IANA Considerations ............................................38 + 7.1. Pre-Authentication and Typed Data .........................38 + 7.2. Fast Armor Types ..........................................40 + 7.3. FAST Options ..............................................40 + 8. Security Considerations ........................................41 + 9. Acknowledgements ...............................................42 + 10. References ....................................................43 + 10.1. Normative References .....................................43 + 10.2. Informative References ...................................43 + Appendix A. Test Vectors for KRB-FX-CF2 ...........................45 + Appendix B. ASN.1 Module ..........................................46 + + + + + +Hartman & Zhu Standards Track [Page 3] + +RFC 6113 Kerberos Preauth Framework April 2011 + + +1. Introduction + + The core Kerberos specification [RFC4120] treats pre-authentication + data (padata) as an opaque typed hole in the messages to the key + distribution center (KDC) that may influence the reply key used to + encrypt the KDC reply. This generality has been useful: pre- + authentication data is used for a variety of extensions to the + protocol, many outside the expectations of the initial designers. + However, this generality makes designing more common types of pre- + authentication mechanisms difficult. Each mechanism needs to specify + how it interacts with other mechanisms. Also, tasks such as + combining a key with the long-term secrets or proving the identity of + the user are common to multiple mechanisms. Where there are + generally well-accepted solutions to these problems, it is desirable + to standardize one of these solutions so mechanisms can avoid + duplication of work. In other cases, a modular approach to these + problems is appropriate. The modular approach will allow new and + better solutions to common pre-authentication problems to be used by + existing mechanisms as they are developed. + + This document specifies a framework for Kerberos pre-authentication + mechanisms. It defines the common set of functions that pre- + authentication mechanisms perform as well as how these functions + affect the state of the request and reply. In addition, several + common tools needed by pre-authentication mechanisms are provided. + Unlike [RFC3961], this framework is not complete -- it does not + describe all the inputs and outputs for the pre-authentication + mechanisms. Pre-authentication mechanism designers should try to be + consistent with this framework because doing so will make their + mechanisms easier to implement. Kerberos implementations are likely + to have plug-in architectures for pre-authentication; such + architectures are likely to support mechanisms that follow this + framework plus commonly used extensions. This framework also + facilitates combining multiple pre-authentication mechanisms, each of + which may represent an authentication factor, into a single multi- + factor pre-authentication mechanism. + + One of these common tools is the flexible authentication secure + tunneling (FAST) padata type. FAST provides a protected channel + between the client and the key distribution center (KDC), and it can + optionally deliver key material used to strengthen the reply key + within the protected channel. Based on FAST, pre-authentication + mechanisms can extend Kerberos with ease, to support, for example, + password-authenticated key exchange (PAKE) protocols with zero- + knowledge password proof (ZKPP) [EKE] [IEEE1363.2]. Any pre- + authentication mechanism can be encapsulated in the FAST messages as + defined in Section 5.4. A pre-authentication type carried within + FAST is called a "FAST factor". Creating a FAST factor is the + + + +Hartman & Zhu Standards Track [Page 4] + +RFC 6113 Kerberos Preauth Framework April 2011 + + + easiest path to create a new pre-authentication mechanism. FAST + factors are significantly easier to analyze from a security + standpoint than other pre-authentication mechanisms. + + Mechanism designers should design FAST factors, instead of new pre- + authentication mechanisms outside of FAST. + +1.1. Conventions and Terminology Used in This Document + + The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", + "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this + document are to be interpreted as described in [RFC2119]. + + This document should be read only after reading the documents + describing the Kerberos cryptography framework [RFC3961] and the core + Kerberos protocol [RFC4120]. This document may freely use + terminology and notation from these documents without reference or + further explanation. + + The word padata is used as a shorthand for pre-authentication data. + + A conversation is the set of all authentication messages exchanged + between the client and the client's Authentication Service (AS) in + order to authenticate the client principal. A conversation as + defined here consists of all messages that are necessary to complete + the authentication between the client and the client's AS. In the + Ticket Granting Service (TGS) exchange, a conversation consists of + the request message and the reply message. The term conversation is + defined here for both AS and TGS for convenience of discussion. See + Section 5.2 for specific rules on the extent of a conversation in the + AS-REQ case. Prior to this framework, implementations needed to use + implementation-specific heuristics to determine the extent of a + conversation. + + If the KDC reply in an AS exchange is verified, the KDC is + authenticated by the client. In this document, verification of the + KDC reply is used as a synonym of authentication of the KDC. + +1.2. Conformance Requirements + + This section summarizes the mandatory-to-implement subset of this + specification as a convenience to implementors. The actual + requirements and their context are stated in the body of the + document. + + Clients conforming to this specification MUST support the padata + defined in Section 5.2. + + + + +Hartman & Zhu Standards Track [Page 5] + +RFC 6113 Kerberos Preauth Framework April 2011 + + + Conforming implementations MUST support Kerberos FAST padata + (Section 5.4). Conforming implementations MUST implement the + FX_FAST_ARMOR_AP_REQUEST armor type. + + Conforming implementations MUST support the encrypted challenge FAST + factor (Section 5.4.6). + +2. Model for Pre-Authentication + + When a Kerberos client wishes to obtain a ticket, it sends an initial + Authentication Service (AS) request to the KDC. If pre- + authentication is required but not being used, then the KDC will + respond with a KDC_ERR_PREAUTH_REQUIRED error [RFC4120]. + Alternatively, if the client knows what pre-authentication to use, it + MAY optimize away a round trip and send an initial request with + padata included in the initial request. If the client includes the + padata computed using the wrong pre-authentication mechanism or + incorrect keys, the KDC MAY return KDC_ERR_PREAUTH_FAILED with no + indication of what padata should have been included. In that case, + the client MUST retry with no padata and examine the error data of + the KDC_ERR_PREAUTH_REQUIRED error. If the KDC includes pre- + authentication information in the accompanying error data of + KDC_ERR_PREAUTH_FAILED, the client SHOULD process the error data and + then retry. + + The conventional KDC maintains no state between two requests; + subsequent requests may even be processed by a different KDC. On the + other hand, the client treats a series of exchanges with KDCs as a + single conversation. Each exchange accumulates state and hopefully + brings the client closer to a successful authentication. + + These models for state management are in apparent conflict. For many + of the simpler pre-authentication scenarios, the client uses one + round trip to find out what mechanisms the KDC supports. Then, the + next request contains sufficient pre-authentication for the KDC to be + able to return a successful reply. For these simple scenarios, the + client only sends one request with pre-authentication data and so the + conversation is trivial. For more complex conversations, the KDC + needs to provide the client with a cookie to include in future + requests to capture the current state of the authentication session. + Handling of multiple round-trip mechanisms is discussed in + Section 5.2. + + This framework specifies the behavior of Kerberos pre-authentication + mechanisms used to identify users or to modify the reply key used to + encrypt the KDC reply. The PA-DATA typed hole may be used to carry + extensions to Kerberos that have nothing to do with proving the + + + + +Hartman & Zhu Standards Track [Page 6] + +RFC 6113 Kerberos Preauth Framework April 2011 + + + identity of the user or establishing a reply key. Such extensions + are outside the scope of this framework. However, mechanisms that do + accomplish these goals should follow this framework. + + This framework specifies the minimum state that a Kerberos + implementation needs to maintain while handling a request in order to + process pre-authentication. It also specifies how Kerberos + implementations process the padata at each step of the AS request + process. + +2.1. Information Managed by the Pre-Authentication Model + + The following information is maintained by the client and KDC as each + request is being processed: + + o The reply key used to encrypt the KDC reply + + o How strongly the identity of the client has been authenticated + + o Whether the reply key has been used in this conversation + + o Whether the reply key has been replaced in this conversation + + o Whether the origin of the KDC reply can be verified by the client + (i.e., whether the KDC is authenticated to the client) + + Conceptually, the reply key is initially the long-term key of the + principal. However, principals can have multiple long-term keys + because of support for multiple encryption types, salts, and + string2key parameters. As described in Section 5.2.7.5 of the + Kerberos protocol [RFC4120], the KDC sends PA-ETYPE-INFO2 to notify + the client what types of keys are available. Thus, in full + generality, the reply key in the pre-authentication model is actually + a set of keys. At the beginning of a request, it is initialized to + the set of long-term keys advertised in the PA-ETYPE-INFO2 element on + the KDC. If multiple reply keys are available, the client chooses + which one to use. Thus, the client does not need to treat the reply + key as a set. At the beginning of a request, the client picks a key + to use. + + KDC implementations MAY choose to offer only one key in the PA-ETYPE- + INFO2 element. Since the KDC already knows the client's list of + supported enctypes from the request, no interoperability problems are + + + + + + + + +Hartman & Zhu Standards Track [Page 7] + +RFC 6113 Kerberos Preauth Framework April 2011 + + + created by choosing a single possible reply key. This way, the KDC + implementation avoids the complexity of treating the reply key as a + set. + + When the padata in the request are verified by the KDC, then the + client is known to have that key; therefore, the KDC SHOULD pick the + same key as the reply key. + + At the beginning of handling a message on both the client and the + KDC, the client's identity is not authenticated. A mechanism may + indicate that it has successfully authenticated the client's + identity. It is useful to keep track of this information on the + client in order to know what pre-authentication mechanisms should be + used. The KDC needs to keep track of whether the client is + authenticated because the primary purpose of pre-authentication is to + authenticate the client identity before issuing a ticket. The + handling of authentication strength using various authentication + mechanisms is discussed in Section 5.5. + + Initially, the reply key is not used. A pre-authentication mechanism + that uses the reply key to encrypt or checksum some data in the + generation of new keys MUST indicate that the reply key is used. + This state is maintained by the client and the KDC to enforce the + security requirement stated in Section 3.3 that the reply key SHOULD + NOT be replaced after it is used. + + Initially, the reply key is not replaced. If a mechanism implements + the Replace Reply Key facility discussed in Section 3.3, then the + state MUST be updated to indicate that the reply key has been + replaced. Once the reply key has been replaced, knowledge of the + reply key is insufficient to authenticate the client. The reply key + is marked as replaced in exactly the same situations as the KDC reply + is marked as not being verified to the client principal. However, + while mechanisms can verify the KDC reply to the client, once the + reply key is replaced, then the reply key remains replaced for the + remainder of the conversation. + + Without pre-authentication, the client knows that the KDC reply is + authentic and has not been modified because it is encrypted in a + long-term key of the client. Only the KDC and the client know that + key. So, at the start of a conversation, the KDC reply is presumed + to be verified using the client's long-term key. It should be noted + that in this document, verifying the KDC reply means authenticating + the KDC, and these phrases are used interchangeably. Any pre- + authentication mechanism that sets a new reply key not based on the + principal's long-term secret MUST either verify the KDC reply some + other way or indicate that the reply is not verified. If a mechanism + indicates that the reply is not verified, then the client + + + +Hartman & Zhu Standards Track [Page 8] + +RFC 6113 Kerberos Preauth Framework April 2011 + + + implementation MUST return an error unless a subsequent mechanism + verifies the reply. The KDC needs to track this state so it can + avoid generating a reply that is not verified. + + In this specification, KDC verification/authentication refers to the + level of authentication of the KDC to the client provided by RFC + 4120. There is a stronger form of KDC verification that, while + sometimes important in Kerberos deployments, is not addressed in this + specification: the typical Kerberos request does not provide a way + for the client machine to know that it is talking to the correct KDC. + Someone who can inject packets into the network between the client + machine and the KDC and who knows the password that the user will + give to the client machine can generate a KDC reply that will decrypt + properly. So, if the client machine needs to authenticate that the + user is in fact the named principal, then the client machine needs to + do a TGS request for itself as a service. Some pre-authentication + mechanisms may provide a way for the client machine to authenticate + the KDC. Examples of this include signing the reply that can be + verified using a well-known public key or providing a ticket for the + client machine as a service. + +2.2. Initial Pre-Authentication Required Error + + Typically, a client starts a conversation by sending an initial + request with no pre-authentication. If the KDC requires pre- + authentication, then it returns a KDC_ERR_PREAUTH_REQUIRED message. + After the first reply with the KDC_ERR_PREAUTH_REQUIRED error code, + the KDC returns the error code KDC_ERR_MORE_PREAUTH_DATA_REQUIRED + (defined in Section 5.2) for pre-authentication configurations that + use multi-round-trip mechanisms; see Section 2.4 for details of that + case. + + The KDC needs to choose which mechanisms to offer the client. The + client needs to be able to choose what mechanisms to use from the + first message. For example, consider the KDC that will accept + mechanism A followed by mechanism B or alternatively the single + mechanism C. A client that supports A and C needs to know that it + should not bother trying A. + + Mechanisms can either be sufficient on their own or can be part of an + authentication set -- a group of mechanisms that all need to + successfully complete in order to authenticate a client. Some + mechanisms may only be useful in authentication sets; others may be + useful alone or in authentication sets. For the second group of + mechanisms, KDC policy dictates whether the mechanism will be part of + an authentication set, offered alone, or both. For each mechanism + that is offered alone (even if it is also offered in an + authentication set), the KDC includes the pre-authentication type ID + + + +Hartman & Zhu Standards Track [Page 9] + +RFC 6113 Kerberos Preauth Framework April 2011 + + + of the mechanism in the padata sequence returned in the + KDC_ERR_PREAUTH_REQUIRED error. Mechanisms that are only offered as + part of an authentication set are not directly represented in the + padata sequence returned in the KDC_ERR_PREAUTH_REQUIRED error, + although they are represented in the PA-AUTHENTICATION-SET sequence. + + The KDC SHOULD NOT send data that is encrypted in the long-term + password-based key of the principal. Doing so has the same security + exposures as the Kerberos protocol without pre-authentication. There + are few situations where the KDC needs to expose cipher text + encrypted in a weak key before the client has proven knowledge of + that key, and where pre-authentication is desirable. + +2.3. Client to KDC + + This description assumes that a client has already received a + KDC_ERR_PREAUTH_REQUIRED from the KDC. If the client performs + optimistic pre-authentication, then the client needs to guess values + for the information it would normally receive from that error + response or use cached information obtained in prior interactions + with the KDC. + + The client starts by initializing the pre-authentication state as + specified. It then processes the padata in the + KDC_ERR_PREAUTH_REQUIRED. + + When processing the response to the KDC_ERR_PREAUTH_REQUIRED, the + client MAY ignore any padata it chooses unless doing so violates a + specification to which the client conforms. Clients conforming to + this specification MUST NOT ignore the padata defined in Section 5.2. + Clients SHOULD choose one authentication set or mechanism that could + lead to authenticating the user and ignore other such mechanisms. + However, this rule does not affect the processing of padata unrelated + to this framework; clients SHOULD process such padata normally. + Since the list of mechanisms offered by the KDC is in the decreasing + preference order, clients typically choose the first mechanism or + authentication set that the client can usefully perform. If a client + chooses to ignore padata, it MUST NOT process the padata, allow the + padata to affect the pre-authentication state, or respond to the + padata. + + For each instance of padata the client chooses to process, the client + processes the padata and modifies the pre-authentication state as + required by that mechanism. + + + + + + + +Hartman & Zhu Standards Track [Page 10] + +RFC 6113 Kerberos Preauth Framework April 2011 + + + After processing the padata in the KDC error, the client generates a + new request. It processes the pre-authentication mechanisms in the + order in which they will appear in the next request, updating the + state as appropriate. The request is sent when it is complete. + +2.4. KDC to Client + + When a KDC receives an AS request from a client, it needs to + determine whether it will respond with an error or an AS reply. + There are many causes for an error to be generated that have nothing + to do with pre-authentication; they are discussed in the core + Kerberos specification. + + From the standpoint of evaluating the pre-authentication, the KDC + first starts by initializing the pre-authentication state. If a PA- + FX-COOKIE pre-authentication data item is present, it is processed + first; see Section 5.2 for a definition. It then processes the + padata in the request. As mentioned in Section 2.3, the KDC MAY + ignore padata that are inappropriate for the configuration and MUST + ignore padata of an unknown type. The KDC MUST NOT ignore padata of + types used in previous messages. For example, if a KDC issues a + KDC_ERR_PREAUTH_REQUIRED error including padata of type x, then the + KDC cannot ignore padata of type x received in an AS-REQ message from + the client. + + At this point, the KDC decides whether it will issue an error or a + reply. Typically, a KDC will issue a reply if the client's identity + has been authenticated to a sufficient degree. + + In the case of a KDC_ERR_MORE_PREAUTH_DATA_REQUIRED error, the KDC + first starts by initializing the pre-authentication state. Then, it + processes any padata in the client's request in the order provided by + the client. Mechanisms that are not understood by the KDC are + ignored. Next, it generates padata for the error response, modifying + the pre-authentication state appropriately as each mechanism is + processed. The KDC chooses the order in which it will generate + padata (and thus the order of padata in the response), but it needs + to modify the pre-authentication state consistently with the choice + of order. For example, if some mechanism establishes an + authenticated client identity, then the subsequent mechanisms in the + generated response receive this state as input. After the padata are + generated, the error response is sent. Typically, the errors with + the code KDC_ERR_MORE_PREAUTH_DATA_REQUIRED in a conversation will + include KDC state, as discussed in Section 5.2. + + To generate a final reply, the KDC generates the padata modifying the + pre-authentication state as necessary. Then, it generates the final + response, encrypting it in the current pre-authentication reply key. + + + +Hartman & Zhu Standards Track [Page 11] + +RFC 6113 Kerberos Preauth Framework April 2011 + + +3. Pre-Authentication Facilities + + Pre-authentication mechanisms can be thought of as providing various + conceptual facilities. This serves two useful purposes. First, + mechanism authors can choose only to solve one specific small + problem. It is often useful for a mechanism designed to offer key + management not to directly provide client authentication but instead + to allow one or more other mechanisms to handle this need. Secondly, + thinking about the abstract services that a mechanism provides yields + a minimum set of security requirements that all mechanisms providing + that facility must meet. These security requirements are not + complete; mechanisms will have additional security requirements based + on the specific protocol they employ. + + A mechanism is not constrained to only offering one of these + facilities. While such mechanisms can be designed and are sometimes + useful, many pre-authentication mechanisms implement several + facilities. It is often easier to construct a secure, simple + solution by combining multiple facilities in a single mechanism than + by solving the problem in full generality. Even when mechanisms + provide multiple facilities, they need to meet the security + requirements for all the facilities they provide. If the FAST factor + approach is used, it is likely that one or a small number of + facilities can be provided by a single mechanism without complicating + the security analysis. + + According to Kerberos extensibility rules (Section 1.5 of the + Kerberos specification [RFC4120]), an extension MUST NOT change the + semantics of a message unless a recipient is known to understand that + extension. Because a client does not know that the KDC supports a + particular pre-authentication mechanism when it sends an initial + request, a pre-authentication mechanism MUST NOT change the semantics + of the request in a way that will break a KDC that does not + understand that mechanism. Similarly, KDCs MUST NOT send messages to + clients that affect the core semantics unless the client has + indicated support for the message. + + The only state in this model that would break the interpretation of a + message is changing the expected reply key. If one mechanism changed + the reply key and a later mechanism used that reply key, then a KDC + that interpreted the second mechanism but not the first would fail to + interpret the request correctly. In order to avoid this problem, + extensions that change core semantics are typically divided into two + parts. The first part proposes a change to the core semantic -- for + example, proposes a new reply key. The second part acknowledges that + the extension is understood and that the change takes effect. + Section 3.2 discusses how to design mechanisms that modify the reply + key to be split into a proposal and acceptance without requiring + + + +Hartman & Zhu Standards Track [Page 12] + +RFC 6113 Kerberos Preauth Framework April 2011 + + + additional round trips to use the new reply key in subsequent pre- + authentication. Other changes in the state described in Section 2.1 + can safely be ignored by a KDC that does not understand a mechanism. + Mechanisms that modify the behavior of the request outside the scope + of this framework need to carefully consider the Kerberos + extensibility rules to avoid similar problems. + +3.1. Client Authentication Facility + + The Client Authentication facility proves the identity of a user to + the KDC before a ticket is issued. Examples of mechanisms + implementing this facility include the encrypted timestamp facility, + defined in Section 5.2.7.2 of the Kerberos specification [RFC4120]. + Mechanisms that provide this facility are expected to mark the client + as authenticated. + + Mechanisms implementing this facility SHOULD require the client to + prove knowledge of the reply key before transmitting a successful KDC + reply. Otherwise, an attacker can intercept the pre-authentication + exchange and get a reply to attack. One way of proving the client + knows the reply key is to implement the Replace Reply Key facility + along with this facility. The Public Key Cryptography for Initial + Authentication in Kerberos (PKINIT) mechanism [RFC4556] implements + Client Authentication alongside Replace Reply Key. + + If the reply key has been replaced, then mechanisms such as + encrypted-timestamp that rely on knowledge of the reply key to + authenticate the client MUST NOT be used. + +3.2. Strengthening Reply Key Facility + + Particularly when dealing with keys based on passwords, it is + desirable to increase the strength of the key by adding additional + secrets to it. Examples of sources of additional secrets include the + results of a Diffie-Hellman key exchange or key bits from the output + of a smart card [KRB-WG.SAM]. Typically, these additional secrets + can be first combined with the existing reply key and then converted + to a protocol key using tools defined in Section 5.1. + + Typically, a mechanism implementing this facility will know that the + other side of the exchange supports the facility before the reply key + is changed. For example, a mechanism might need to learn the + certificate for a KDC before encrypting a new key in the public key + belonging to that certificate. However, if a mechanism implementing + this facility wishes to modify the reply key before knowing that the + other party in the exchange supports the mechanism, it proposes + modifying the reply key. The other party then includes a message + indicating that the proposal is accepted if it is understood and + + + +Hartman & Zhu Standards Track [Page 13] + +RFC 6113 Kerberos Preauth Framework April 2011 + + + meets policy. In many cases, it is desirable to use the new reply + key for client authentication and for other facilities. Waiting for + the other party to accept the proposal and actually modify the reply + key state would add an additional round trip to the exchange. + Instead, mechanism designers are encouraged to include a typed hole + for additional padata in the message that proposes the reply key + change. The padata included in the typed hole are generated assuming + the new reply key. If the other party accepts the proposal, then + these padata are considered as an inner level. As with the outer + level, one authentication set or mechanism is typically chosen for + client authentication, along with auxiliary mechanisms such as KDC + cookies, and other mechanisms are ignored. When mechanisms include + such a container, the hint provided for use in authentication sets + (as defined in Section 5.3) MUST contain a sequence of inner + mechanisms along with hints for those mechanisms. The party + generating the proposal can determine whether the padata were + processed based on whether the proposal for the reply key is + accepted. + + The specific formats of the proposal message, including where padata + are included, is a matter for the mechanism specification. + Similarly, the format of the message accepting the proposal is + mechanism specific. + + Mechanisms implementing this facility and including a typed hole for + additional padata MUST checksum that padata using a keyed checksum or + encrypt the padata. This requirement protects against modification + of the contents of the typed hole. By modifying these contents, an + attacker might be able to choose which mechanism is used to + authenticate the client, or to convince a party to provide text + encrypted in a key that the attacker had manipulated. It is + important that mechanisms strengthen the reply key enough that using + it to checksum padata is appropriate. + +3.3. Replace Reply Key Facility + + The Replace Reply Key facility replaces the key in which a successful + AS reply will be encrypted. This facility can only be used in cases + where knowledge of the reply key is not used to authenticate the + client. The new reply key MUST be communicated to the client and the + KDC in a secure manner. This facility MUST NOT be used if there can + be a man-in-the-middle between the client and the KDC. Mechanisms + implementing this facility MUST mark the reply key as replaced in the + pre-authentication state. Mechanisms implementing this facility MUST + either provide a mechanism to verify the KDC reply to the client or + mark the reply as unverified in the pre-authentication state. + Mechanisms implementing this facility SHOULD NOT be used if a + previous mechanism has used the reply key. + + + +Hartman & Zhu Standards Track [Page 14] + +RFC 6113 Kerberos Preauth Framework April 2011 + + + As with the Strengthening Reply Key facility, Kerberos extensibility + rules require that the reply key not be changed unless both sides of + the exchange understand the extension. In the case of this facility, + it will likely be the case for both sides to know that the facility + is available by the time that the new key is available to be used. + However, mechanism designers can use a container for padata in a + proposal message, as discussed in Section 3.2, if appropriate. + +3.4. KDC Authentication Facility + + This facility verifies that the reply comes from the expected KDC. + In traditional Kerberos, the KDC and the client share a key, so if + the KDC reply can be decrypted, then the client knows that a trusted + KDC responded. Note that the client machine cannot trust the client + unless the machine is presented with a service ticket for it + (typically, the machine can retrieve this ticket by itself). + However, if the reply key is replaced, some mechanism is required to + verify the KDC. Pre-authentication mechanisms providing this + facility allow a client to determine that the expected KDC has + responded even after the reply key is replaced. They mark the pre- + authentication state as having been verified. + +4. Requirements for Pre-Authentication Mechanisms + + This section lists requirements for specifications of pre- + authentication mechanisms. + + For each message in the pre-authentication mechanism, the + specification describes the pa-type value to be used and the contents + of the message. The processing of the message by the sender and + recipient is also specified. This specification needs to include all + modifications to the pre-authentication state. + + Generally, mechanisms have a message that can be sent in the error + data of the KDC_ERR_PREAUTH_REQUIRED error message or in an + authentication set. If the client needs information, such as trusted + certificate authorities, in order to determine if it can use the + mechanism, then this information should be in that message. In + addition, such mechanisms should also define a pa-hint to be included + in authentication sets. Often, the same information included in the + padata-value is appropriate to include in the pa-hint (as defined in + Section 5.3). + + In order to ease security analysis, the mechanism specification + should describe what facilities from this document are offered by the + mechanism. For each facility, the security considerations section of + the mechanism specification should show that the security + + + + +Hartman & Zhu Standards Track [Page 15] + +RFC 6113 Kerberos Preauth Framework April 2011 + + + requirements of that facility are met. This requirement is + applicable to any FAST factor that provides authentication + information. + + Significant problems have resulted in the specification of Kerberos + protocols because much of the KDC exchange is not protected against + alteration. The security considerations section should discuss + unauthenticated plaintext attacks. It should either show that + plaintext is protected or discuss what harm an attacker could do by + modifying the plaintext. It is generally acceptable for an attacker + to be able to cause the protocol negotiation to fail by modifying + plaintext. More significant attacks should be evaluated carefully. + + As discussed in Section 5.2, there is no guarantee that a client will + use the same KDCs for all messages in a conversation. The mechanism + specification needs to show why the mechanism is secure in this + situation. The hardest problem to deal with, especially for + challenge/response mechanisms is to make sure that the same response + cannot be replayed against two KDCs while allowing the client to talk + to any KDC. + +4.1. Protecting Requests/Responses + + Mechanism designers SHOULD protect cleartext portions of pre- + authentication data. Various denial-of-service attacks and downgrade + attacks against Kerberos are possible unless plaintexts are somehow + protected against modification. An early design goal of Kerberos + Version 5 [RFC4120] was to avoid encrypting more of the + authentication exchange than was required. (Version 4 doubly- + encrypted the encrypted part of a ticket in a KDC reply, for + example). This minimization of encryption reduces the load on the + KDC and busy servers. Also, during the initial design of Version 5, + the existence of legal restrictions on the export of cryptography + made it desirable to minimize of the number of uses of encryption in + the protocol. Unfortunately, performing this minimization created + numerous instances of unauthenticated security-relevant plaintext + fields. + + Mechanisms MUST guarantee that by the end of a successful + authentication exchange, both the client and the KDC have verified + all the plaintext sent by the other party. If there is more than one + round trip in the exchange, mechanisms MUST additionally guarantee + that no individual messages were reordered or replayed from a + previous exchange. Strategies for accomplishing this include using + message authentication codes (MACs) to protect the plaintext as it is + sent including some form of nonce or cookie to allow for the chaining + of state from one message to the next or exchanging a MAC of the + entire conversation after a key is established. + + + +Hartman & Zhu Standards Track [Page 16] + +RFC 6113 Kerberos Preauth Framework April 2011 + + + Mechanism designers need to provide a strategy for updating + cryptographic algorithms, such as defining a new pre-authentication + type for each algorithm or taking advantage of the client's list of + supported RFC 3961 encryption types to indicate the client's support + for cryptographic algorithms. + + Primitives defined in [RFC3961] are RECOMMENDED for integrity + protection and confidentiality. Mechanisms based on these primitives + are crypto-agile as the result of using [RFC3961] along with + [RFC4120]. The advantage afforded by crypto-agility is the ability + to incrementally deploy a fix specific to a particular algorithm thus + avoid a multi-year standardization and deployment cycle, when real + attacks do arise against that algorithm. + + Note that data used by FAST factors (defined in Section 5.4) is + encrypted in a protected channel; thus, they do not share the un- + authenticated-text issues with mechanisms designed as full-blown pre- + authentication mechanisms. + +5. Tools for Use in Pre-Authentication Mechanisms + + This section describes common tools needed by multiple pre- + authentication mechanisms. By using these tools, mechanism designers + can use a modular approach to specify mechanism details and ease + security analysis. + +5.1. Combining Keys + + Frequently, a weak key needs to be combined with a stronger key + before use. For example, passwords are typically limited in size and + insufficiently random: therefore, it is desirable to increase the + strength of the keys based on passwords by adding additional secrets. + An additional source of secrecy may come from hardware tokens. + + This section provides standard ways to combine two keys into one. + + KRB-FX-CF1() is defined to combine two passphrases. + + KRB-FX-CF1(UTF-8 string, UTF-8 string) -> (UTF-8 string) + KRB-FX-CF1(x, y) := x || y + + Where || denotes concatenation. The strength of the final key is + roughly the total strength of the individual keys being combined, + assuming that the string_to_key() function [RFC3961] uses all its + input evenly. + + + + + + +Hartman & Zhu Standards Track [Page 17] + +RFC 6113 Kerberos Preauth Framework April 2011 + + + An example usage of KRB-FX-CF1() is when a device provides random but + short passwords, the password is often combined with a personal + identification number (PIN). The password and the PIN can be + combined using KRB-FX-CF1(). + + KRB-FX-CF2() combines two protocol keys based on the pseudo-random() + function defined in [RFC3961]. + + Given two input keys, K1 and K2, where K1 and K2 can be of two + different enctypes, the output key of KRB-FX-CF2(), K3, is derived as + follows: + + KRB-FX-CF2(protocol key, protocol key, octet string, + octet string) -> (protocol key) + + PRF+(K1, pepper1) -> octet-string-1 + PRF+(K2, pepper2) -> octet-string-2 + KRB-FX-CF2(K1, K2, pepper1, pepper2) := + random-to-key(octet-string-1 ^ octet-string-2) + + Where ^ denotes the exclusive-OR operation. PRF+() is defined as + follows: + + PRF+(protocol key, octet string) -> (octet string) + + PRF+(key, shared-info) := pseudo-random( key, 1 || shared-info ) || + pseudo-random( key, 2 || shared-info ) || + pseudo-random( key, 3 || shared-info ) || ... + + Here the counter value 1, 2, 3, and so on are encoded as a one-octet + integer. The pseudo-random() operation is specified by the enctype + of the protocol key. PRF+() uses the counter to generate enough bits + as needed by the random-to-key() [RFC3961] function for the + encryption type specified for the resulting key; unneeded bits are + removed from the tail. Unless otherwise specified, the resulting + enctype of KRB-FX-CF2 is the enctype of k1. The pseudo-random() + operation is the RFC 3961 pseudo-random() operation for the + corresponding input key; the random-to-key() operation is the RFC + 3961 random-to-key operation for the resulting key. + + Mechanism designers MUST specify the values for the input parameter + pepper1 and pepper2 when combining two keys using KRB-FX-CF2(). The + pepper1 and pepper2 MUST be distinct so that if the two keys being + combined are the same, the resulting key is not a trivial key. + + + + + + + +Hartman & Zhu Standards Track [Page 18] + +RFC 6113 Kerberos Preauth Framework April 2011 + + +5.2. Managing States for the KDC + + Kerberos KDCs are stateless in that there is no requirement that + clients will choose the same KDC for the second request in a + conversation. Proxies or other intermediate nodes may also influence + KDC selection. So, each request from a client to a KDC must include + sufficient information that the KDC can regenerate any needed state. + This is accomplished by giving the client a potentially long opaque + cookie in responses to include in future requests in the same + conversation. The KDC MAY respond that a conversation is too old and + needs to restart by responding with a KDC_ERR_PREAUTH_EXPIRED error. + + KDC_ERR_PREAUTH_EXPIRED 90 + + When a client receives this error, the client SHOULD abort the + existing conversation, and restart a new one. + + An example, where more than one message from the client is needed, is + when the client is authenticated based on a challenge/response + scheme. In that case, the KDC needs to keep track of the challenge + issued for a client authentication request. + + The PA-FX-COOKIE padata type is defined in this section to facilitate + state management in the AS exchange. These padata are sent by the + KDC when the KDC requires state for a future transaction. The client + includes this opaque token in the next message in the conversation. + The token may be relatively large; clients MUST be prepared for + tokens somewhat larger than the size of all messages in a + conversation. + + PA-FX-COOKIE 133 + -- Stateless cookie that is not tied to a specific KDC. + + The corresponding padata-value field [RFC4120] contains an opaque + token that will be echoed by the client in its response to an error + from the KDC. + + The cookie token is generated by the KDC and transmitted in a PA-FX- + COOKIE pre-authentication data item of a KRB-ERROR message. The + client MUST copy the exact cookie encapsulated in a PA-FX-COOKIE data + element into the next message of the same conversation. The content + of the cookie field is a local matter of the KDC. As a result, it is + not generally possible to mix KDC implementations from different + vendors in the same realm. However, the KDC MUST construct the + cookie token in such a manner that a malicious client cannot subvert + the authentication process by manipulating the token. The KDC + implementation needs to consider expiration of tokens, key rollover, + and other security issues in token design. The content of the cookie + + + +Hartman & Zhu Standards Track [Page 19] + +RFC 6113 Kerberos Preauth Framework April 2011 + + + field is likely specific to the pre-authentication mechanisms used to + authenticate the client. If a client authentication response can be + replayed to multiple KDCs via the PA-FX-COOKIE mechanism, an + expiration in the cookie is RECOMMENDED to prevent the response being + presented indefinitely. Implementations need to consider replay both + of an entire conversation and of messages within a conversation when + designing what information is stored in a cookie and how pre- + authentication mechanisms are implemented. + + If at least one more message for a mechanism or a mechanism set is + expected by the KDC, the KDC returns a + KDC_ERR_MORE_PREAUTH_DATA_REQUIRED error with a PA-FX-COOKIE to + identify the conversation with the client, according to Section 2.2. + The cookie is not expected to stay constant for a conversation: the + KDC is expected to generate a new cookie for each message. + + KDC_ERR_MORE_PREAUTH_DATA_REQUIRED 91 + + A client MAY throw away the state associated with a conversation and + begin a new conversation by discarding its state and not including a + cookie in the first message of a conversation. KDCs that comply with + this specification MUST include a cookie in a response when the + client can continue the conversation. In particular, a KDC MUST + include a cookie in a KDC_ERR_PREAUTH_REQUIRED or + KDC_ERR_MORE_PREAUTH_DATA_REQUIRED. KDCs SHOULD include a cookie in + errors containing additional information allowing a client to retry. + One reasonable strategy for meeting these requirements is to always + include a cookie in KDC errors. + + A KDC MAY indicate that it is terminating a conversation by not + including a cookie in a response. When FAST is used, clients can + assume that the absence of a cookie means that the KDC is ending the + conversation. Similarly, if a cookie is seen at all during a + conversation, clients MAY assume that the absence of a cookie in a + future message means that the KDC is ending the conversation. + Clients also need to deal with KDCs, prior to this specification, + that do not include cookies; if neither cookies nor FAST are used in + a conversation, the absence of a cookie is not a strong indication + that the KDC is terminating the conversation. + +5.3. Pre-Authentication Set + + If all mechanisms in a group need to successfully complete in order + to authenticate a client, the client and the KDC SHOULD use the PA- + AUTHENTICATION-SET padata element. + + PA-AUTHENTICATION-SET 134 + + + + +Hartman & Zhu Standards Track [Page 20] + +RFC 6113 Kerberos Preauth Framework April 2011 + + + A PA-AUTHENTICATION-SET padata element contains the ASN.1 DER + encoding of the PA-AUTHENTICATION-SET structure: + + PA-AUTHENTICATION-SET ::= SEQUENCE OF PA-AUTHENTICATION-SET-ELEM + + PA-AUTHENTICATION-SET-ELEM ::= SEQUENCE { + pa-type [0] Int32, + -- same as padata-type. + pa-hint [1] OCTET STRING OPTIONAL, + pa-value [2] OCTET STRING OPTIONAL, + ... + } + + The pa-type field of the PA-AUTHENTICATION-SET-ELEM structure + contains the corresponding value of padata-type in PA-DATA [RFC4120]. + Associated with the pa-type is a pa-hint, which is an octet string + specified by the pre-authentication mechanism. This hint may provide + information for the client that helps it determine whether the + mechanism can be used. For example, a public-key mechanism might + include the certificate authorities it trusts in the hint info. Most + mechanisms today do not specify hint info; if a mechanism does not + specify hint info, the KDC MUST NOT send a hint for that mechanism. + To allow future revisions of mechanism specifications to add hint + info, clients MUST ignore hint info received for mechanisms that the + client believes do not support hint info. The pa-value element of + the PA-AUTHENTICATION-SET-ELEM sequence is included to carry the + first padata-value from the KDC to the client. If the client chooses + this authentication set, then the client MUST process this pa-value. + The pa-value element MUST be absent for all but the first entry in + the authentication set. Clients MUST ignore the pa-value for the + second and following entries in the authentication set. + + If the client chooses an authentication set, then its first AS-REQ + message MUST contain a PA-AUTH-SET-SELECTED padata element. This + element contains the encoding of the PA-AUTHENTICATION-SET sequence + received from the KDC corresponding to the authentication set that is + chosen. The client MUST use the same octet values received from the + KDC; it cannot re-encode the sequence. This allows KDCs to use bit- + wise comparison to identify the selected authentication set. + Permitting bit-wise comparison may limit the ability to use certain + pre-authentication mechanisms that generate a dynamic challenge in an + authentication set with optimistic selection of an authentication + set. As with other optimistic pre-authentication failures, the KDC + MAY return KDC_ERR_PREAUTH_FAILED with a new list of pre- + authentication mechanisms (including authentication sets) if + optimistic pre-authentication fails. The PA-AUTH-SET-SELECTED padata + element MUST come before any padata elements from the authentication + set in the padata sequence in the AS-REQ message. The client MAY + + + +Hartman & Zhu Standards Track [Page 21] + +RFC 6113 Kerberos Preauth Framework April 2011 + + + cache authentication sets from prior messages and use them to + construct an optimistic initial AS-REQ. If the KDC receives a PA- + AUTH-SET-SELECTED padata element that does not correspond to an + authentication set that it would offer, then the KDC returns the + KDC_ERR_PREAUTH_BAD_AUTHENTICATION_SET error. The e-data in this + error contains a sequence of padata just as for the + KDC_ERR_PREAUTH_REQUIRED error. + + PA-AUTH-SET-SELECTED 135 + KDC_ERR_PREAUTH_BAD_AUTHENTICATION_SET 92 + + The PA-AUTHENTICATION-SET appears only in the first message from the + KDC to the client. In particular, the client MAY fail if the + authentication mechanism sets change as the conversation progresses. + Clients MAY assume that the hints provided in the authentication set + contain enough information that the client knows what user interface + elements need to be displayed during the entire authentication + conversation. Exceptional circumstances, such as expired passwords + or expired accounts, may require that additional user interface be + displayed. Mechanism designers need to carefully consider the design + of their hints so that the client has this information. This way, + clients can construct necessary dialogue boxes or wizards based on + the authentication set and can present a coherent user interface. + Current standards for user interfaces do not provide an acceptable + experience when the client has to ask additional questions later in + the conversation. + + When indicating which sets of pre-authentication mechanisms are + supported, the KDC includes a PA-AUTHENTICATION-SET padata element + for each pre-authentication mechanism set. + + The client sends the padata-value for the first mechanism it picks in + the pre-authentication set, when the first mechanism completes, the + client and the KDC will proceed with the second mechanism, and so on + until all mechanisms complete successfully. The PA-FX-COOKIE, as + defined in Section 5.2, MUST be sent by the KDC. One reason for this + requirement is so that the conversation can continue if the + conversation involves multiple KDCs. KDCs MUST support clients that + do not include a cookie because they optimistically choose an + authentication set, although they MAY always return a + KDC_ERR_PREAUTH_BAD_AUTHENTICATION_SET and include a cookie in that + message. Clients that support PA-AUTHENTICATION-SET MUST support PA- + FX-COOKIE. + + Before the authentication succeeds and a ticket is returned, the + message that the client sends is an AS-REQ and the message that the + KDC sends is a KRB-ERROR message. The error code in the KRB-ERROR + message from the KDC is KDC_ERR_MORE_PREAUTH_DATA_REQUIRED as defined + + + +Hartman & Zhu Standards Track [Page 22] + +RFC 6113 Kerberos Preauth Framework April 2011 + + + in Section 5.2 and the accompanying e-data contains the DER encoding + of ASN.1 type METHOD-DATA. The KDC includes the padata elements in + the METHOD-DATA. If there are no padata, the e-data field is absent + in the KRB-ERROR message. + + If the client sends the last message for a given mechanism, then the + KDC sends the first message for the next mechanism. If the next + mechanism does not start with a KDC-side challenge, then the KDC + includes a padata item with the appropriate pa-type and an empty pa- + data. + + If the KDC sends the last message for a particular mechanism, the KDC + also includes the first padata for the next mechanism. + +5.4. Definition of Kerberos FAST Padata + + As described in [RFC4120], Kerberos is vulnerable to offline + dictionary attacks. An attacker can request an AS-REP and try + various passwords to see if they can decrypt the resulting ticket. + RFC 4120 provides the encrypted timestamp pre-authentication method + that ameliorates the situation somewhat by requiring that an attacker + observe a successful authentication. However, stronger security is + desired in many environments. The Kerberos FAST pre-authentication + padata defined in this section provides a tool to significantly + reduce vulnerability to offline dictionary attacks. When combined + with encrypted challenge, FAST requires an attacker to mount a + successful man-in-the-middle attack to observe ciphertext. When + combined with host keys, FAST can even protect against active + attacks. FAST also provides solutions to common problems for pre- + authentication mechanisms such as binding of the request and the + reply and freshness guarantee of the authentication. FAST itself, + however, does not authenticate the client or the KDC; instead, it + provides a typed hole to allow pre-authentication data be tunneled. + A pre-authentication data element used within FAST is called a "FAST + factor". A FAST factor captures the minimal work required for + extending Kerberos to support a new pre-authentication scheme. + + A FAST factor MUST NOT be used outside of FAST unless its + specification explicitly allows so. The typed holes in FAST messages + can also be used as generic holes for other padata that are not + intended to prove the client's identity, or establish the reply key. + + New pre-authentication mechanisms SHOULD be designed as FAST factors, + instead of full-blown pre-authentication mechanisms. + + FAST factors that are pre-authentication mechanisms MUST meet the + requirements in Section 4. + + + + +Hartman & Zhu Standards Track [Page 23] + +RFC 6113 Kerberos Preauth Framework April 2011 + + + FAST employs an armoring scheme. The armor can be a Ticket Granting + Ticket (TGT) obtained by the client's machine using the host keys to + pre-authenticate with the KDC, or an anonymous TGT obtained based on + anonymous PKINIT [RFC6112] [RFC4556]. + + The rest of this section describes the types of armors and the syntax + of the messages used by FAST. Conforming implementations MUST + support Kerberos FAST padata. + + Any FAST armor scheme MUST provide a fresh armor key for each + conversation. Clients and KDCs can assume that if a message is + encrypted and integrity protected with a given armor key, then it is + part of the conversation using that armor key. + + All KDCs in a realm MUST support FAST if FAST is offered by any KDC + as a pre-authentication mechanism. + +5.4.1. FAST Armors + + An armor key is used to encrypt pre-authentication data in the FAST + request and the response. The KrbFastArmor structure is defined to + identify the armor key. This structure contains the following two + fields: the armor-type identifies the type of armors and the armor- + value is an OCTET STRING that contains the description of the armor + scheme and the armor key. + + KrbFastArmor ::= SEQUENCE { + armor-type [0] Int32, + -- Type of the armor. + armor-value [1] OCTET STRING, + -- Value of the armor. + ... + } + + The value of the armor key is a matter of the armor type + specification. Only one armor type is defined in this document. + + FX_FAST_ARMOR_AP_REQUEST 1 + + The FX_FAST_ARMOR_AP_REQUEST armor is based on Kerberos tickets. + + Conforming implementations MUST implement the + FX_FAST_ARMOR_AP_REQUEST armor type. If a FAST KDC receives an + unknown armor type it MUST respond with KDC_ERR_PREAUTH_FAILED. + + An armor type may be appropriate for use in armoring AS requests, + armoring TGS requests, or both. TGS armor types MUST authenticate + the client to the KDC, typically by binding the TGT sub-session key + + + +Hartman & Zhu Standards Track [Page 24] + +RFC 6113 Kerberos Preauth Framework April 2011 + + + to the armor key. As discussed below, it is desirable for AS armor + types to authenticate the KDC to the client, but this is not + required. + + FAST implementations MUST maintain state about whether the armor + mechanism authenticates the KDC. If it does not, then a FAST factor + that authenticates the KDC MUST be used if the reply key is replaced. + +5.4.1.1. Ticket-Based Armors + + This is a ticket-based armoring scheme. The armor-type is + FX_FAST_ARMOR_AP_REQUEST, the armor-value contains an ASN.1 DER + encoded AP-REQ. The ticket in the AP-REQ is called an armor ticket + or an armor TGT. The subkey field in the AP-REQ MUST be present. + The armor key is defined by the following function: + + armor_key = KRB-FX-CF2( subkey, ticket_session_key, + "subkeyarmor", "ticketarmor" ) + + The 'ticket_session_key' is the session key from the ticket in the + ap-req. The 'subkey' is the ap-req subkey. This construction + guarantees that both the KDC (through the session key) and the client + (through the subkey) contribute to the armor key. + + The server name field of the armor ticket MUST identify the TGS of + the target realm. Here are three common ways in the decreasing + preference order how an armor TGT SHOULD be obtained: + + 1. If the client is authenticating from a host machine whose + Kerberos realm has an authentication path to the client's realm, + the host machine obtains a TGT by using the host keys. If the + client's realm is different than the realm of the local host, the + machine then obtains a cross-realm TGT to the client's realm as + the armor ticket. Otherwise, the host's primary TGT is the armor + ticket. + + 2. If the client's host machine cannot obtain a host ticket strictly + based on RFC 4120, but the KDC has an asymmetric signing key + whose binding with the expected KDC can be verified by the + client, the client can use anonymous PKINIT [RFC6112] [RFC4556] + to authenticate the KDC and obtain an anonymous TGT as the armor + ticket. The armor ticket can also be a cross-realm TGT obtained + based on the initial primary TGT obtained using anonymous PKINIT + with KDC authentication. + + 3. Otherwise, the client uses anonymous PKINIT to get an anonymous + TGT without KDC authentication and that TGT is the armor ticket. + Note that this mode of operation is vulnerable to man-in-the- + + + +Hartman & Zhu Standards Track [Page 25] + +RFC 6113 Kerberos Preauth Framework April 2011 + + + middle attacks at the time of obtaining the initial anonymous + armor TGT. + + If anonymous PKINIT is used to obtain the armor ticket, the KDC + cannot know whether its signing key can be verified by the client; + hence, the KDC MUST be marked as unverified from the KDC's point of + view while the client could be able to authenticate the KDC by + verifying the KDC's signing key is bound with the expected KDC. The + client needs to carefully consider the risk and benefit tradeoffs + associated with active attacks before exposing cipher text encrypted + using the user's long-term secrets when the armor does not + authenticate the KDC. + + The TGS MUST reject a request if there is an AD-fx-fast-armor (71) + element in the authenticator of the pa-tgs-req padata or if the + ticket in the authenticator of a pa-tgs-req contains the AD-fx-fast- + armor authorization data element. These tickets and authenticators + MAY be used as FAST armor tickets but not to obtain a ticket via the + TGS. This authorization data is used in a system where the + encryption of the user's pre-authentication data is performed in an + unprivileged user process. A privileged process can provide to the + user process a host ticket, an authenticator for use with that + ticket, and the sub-session key contained in the authenticator. In + order for the host process to ensure that the host ticket is not + accidentally or intentionally misused, (i.e., the user process might + use the host ticket to authenticate as the host), it MUST include a + critical authorization data element of the type AD-fx-fast-armor when + providing the authenticator or in the enc-authorization-data field of + the TGS request used to obtain the TGT. The corresponding ad-data + field of the AD-fx-fast-armor element is empty. + + This armor type is only valid for AS requests; implicit armor, + described below in TGS processing, is the only supported way to + establish an armor key for the TGS at this time. + +5.4.2. FAST Request + + A padata type PA-FX-FAST is defined for the Kerberos FAST pre- + authentication padata. The corresponding padata-value field + [RFC4120] contains the DER encoding of the ASN.1 type PA-FX-FAST- + REQUEST. As with all pre-authentication types, the KDC SHOULD + advertise PA-FX-FAST in a PREAUTH_REQUIRED error. KDCs MUST send the + advertisement of PA-FX-FAST with an empty pa-value. Clients MUST + ignore the pa-value of PA-FX-FAST in an initial PREAUTH_REQUIRED + error. FAST is not expected to be used in an authentication set: + clients will typically use FAST padata if available and this decision + should not depend on what other pre-authentication methods are + available. As such, no pa-hint is defined for FAST at this time. + + + +Hartman & Zhu Standards Track [Page 26] + +RFC 6113 Kerberos Preauth Framework April 2011 + + + PA-FX-FAST 136 + -- Padata type for Kerberos FAST + + PA-FX-FAST-REQUEST ::= CHOICE { + armored-data [0] KrbFastArmoredReq, + ... + } + + KrbFastArmoredReq ::= SEQUENCE { + armor [0] KrbFastArmor OPTIONAL, + -- Contains the armor that identifies the armor key. + -- MUST be present in AS-REQ. + req-checksum [1] Checksum, + -- For AS, contains the checksum performed over the type + -- KDC-REQ-BODY for the req-body field of the KDC-REQ + -- structure; + -- For TGS, contains the checksum performed over the type + -- AP-REQ in the PA-TGS-REQ padata. + -- The checksum key is the armor key, the checksum + -- type is the required checksum type for the enctype of + -- the armor key, and the key usage number is + -- KEY_USAGE_FAST_REQ_CHKSUM. + enc-fast-req [2] EncryptedData, -- KrbFastReq -- + -- The encryption key is the armor key, and the key usage + -- number is KEY_USAGE_FAST_ENC. + ... + } + + KEY_USAGE_FAST_REQ_CHKSUM 50 + KEY_USAGE_FAST_ENC 51 + + The PA-FX-FAST-REQUEST structure contains a KrbFastArmoredReq type. + The KrbFastArmoredReq encapsulates the encrypted padata. + + The enc-fast-req field contains an encrypted KrbFastReq structure. + The armor key is used to encrypt the KrbFastReq structure, and the + key usage number for that encryption is KEY_USAGE_FAST_ENC. + + The armor key is selected as follows: + + o In an AS request, the armor field in the KrbFastArmoredReq + structure MUST be present and the armor key is identified + according to the specification of the armor type. + + + + + + + + +Hartman & Zhu Standards Track [Page 27] + +RFC 6113 Kerberos Preauth Framework April 2011 + + + o There are two possibilities for armor for a TGS request. If the + ticket presented in the PA-TGS-REQ authenticator is a TGT, then + the client SHOULD NOT include the armor field in the Krbfastreq + and a subkey MUST be included in the PA-TGS-REQ authenticator. In + this case, the armor key is the same armor key that would be + computed if the TGS-REQ authenticator was used in an + FX_FAST_ARMOR_AP_REQUEST armor. Clients MAY present a non-TGT in + the PA-TGS-REQ authenticator and omit the armor field, in which + case the armor key is the same that would be computed if the + authenticator were used in an FX_FAST_ARMOR_AP_REQUEST armor. + This is the only case where a ticket other than a TGT can be used + to establish an armor key; even though the armor key is computed + the same as an FX_FAST_ARMOR_AP_REQUEST, a non-TGT cannot be used + as an armor ticket in FX_FAST_ARMOR_AP_REQUEST. Alternatively, a + client MAY use an armor type defined in the future for use with + the TGS request. + + The req-checksum field contains a checksum computed differently for + AS and TGS. For an AS-REQ, it is performed over the type KDC-REQ- + BODY for the req-body field of the KDC-REQ structure of the + containing message; for a TGS-REQ, it is performed over the type AP- + REQ in the PA-TGS-REQ padata of the TGS request. The checksum key is + the armor key, and the checksum type is the required checksum type + for the enctype of the armor key per [RFC3961]. This checksum MUST + be a keyed checksum and it is included in order to bind the FAST + padata to the outer request. A KDC that implements FAST will ignore + the outer request, but including a checksum is relatively cheap and + may prevent confusing behavior. + + The KrbFastReq structure contains the following information: + + KrbFastReq ::= SEQUENCE { + fast-options [0] FastOptions, + -- Additional options. + padata [1] SEQUENCE OF PA-DATA, + -- padata typed holes. + req-body [2] KDC-REQ-BODY, + -- Contains the KDC request body as defined in Section + -- 5.4.1 of [RFC4120]. + -- This req-body field is preferred over the outer field + -- in the KDC request. + ... + } + + + + + + + + +Hartman & Zhu Standards Track [Page 28] + +RFC 6113 Kerberos Preauth Framework April 2011 + + + The fast-options field indicates various options that are to modify + the behavior of the KDC. The following options are defined: + + FastOptions ::= KerberosFlags + -- reserved(0), + -- hide-client-names(1), + + Bits Name Description + ----------------------------------------------------------------- + 0 RESERVED Reserved for future expansion of this + field. + 1 hide-client-names Requesting the KDC to hide client + names in the KDC response, as + described next in this section. + 16 kdc-follow-referrals reserved [REFERRALS]. + + Bits 1 through 15 inclusive (with bit 1 and bit 15 included) are + critical options. If the KDC does not support a critical option, it + MUST fail the request with KDC_ERR_UNKNOWN_CRITICAL_FAST_OPTIONS, and + there is no accompanying e-data defined in this document for this + error code. Bit 16 and onward (with bit 16 included) are non- + critical options. KDCs conforming to this specification ignore + unknown non-critical options. + + KDC_ERR_UNKNOWN_CRITICAL_FAST_OPTIONS 93 + + The hide-client-names Option + + The Kerberos response defined in [RFC4120] contains the client + identity in cleartext. This makes traffic analysis + straightforward. The hide-client-names option is designed to + complicate traffic analysis. If the hide-client-names option is + set, the KDC implementing PA-FX-FAST MUST identify the client as + the anonymous principal [RFC6112] in the KDC reply and the error + response. Hence, this option is set by the client if it wishes to + conceal the client identity in the KDC response. A conforming KDC + ignores the client principal name in the outer KDC-REQ-BODY field, + and identifies the client using the cname and crealm fields in the + req-body field of the KrbFastReq structure. + + The kdc-follow-referrals Option + + This option is reserved for [REFERRALS]. + + + + + + + + +Hartman & Zhu Standards Track [Page 29] + +RFC 6113 Kerberos Preauth Framework April 2011 + + + The padata field contains a list of PA-DATA structures as described + in Section 5.2.7 of [RFC4120]. These PA-DATA structures can contain + FAST factors. They can also be used as generic typed-holes to + contain data not intended for proving the client's identity or + establishing a reply key, but for protocol extensibility. If the KDC + supports the PA-FX-FAST-REQUEST padata, unless otherwise specified, + the client MUST place any padata that is otherwise in the outer KDC + request body into this field. In a TGS request, PA-TGS-REQ padata is + not included in this field and it is present in the outer KDC request + body. + + The KDC-REQ-BODY in the FAST structure is used in preference to the + KDC-REQ-BODY outside of the FAST pre-authentication. The outer KDC- + REQ-BODY structure SHOULD be filled in for backwards compatibility + with KDCs that do not support FAST. A conforming KDC ignores the + outer KDC-REQ-BODY field in the KDC request. Pre-authentication data + methods such as [RFC4556] that include a checksum of the KDC-REQ-BODY + should checksum the KDC-REQ-BODY in the FAST structure. + + In a TGS request, a client MAY include the AD-fx-fast-used authdata + either in the pa-tgs-req authenticator or in the authorization data + in the pa-tgs-req ticket. If the KDC receives this authorization + data but does not find a FAST padata, then it MUST return + KRB_APP_ERR_MODIFIED. + +5.4.3. FAST Response + + The KDC that supports the PA-FX-FAST padata MUST include a PA-FX-FAST + padata element in the KDC reply. In the case of an error, the PA-FX- + FAST padata is included in the KDC responses according to + Section 5.4.4. + + The corresponding padata-value field [RFC4120] for the PA-FX-FAST in + the KDC response contains the DER encoding of the ASN.1 type PA-FX- + FAST-REPLY. + + PA-FX-FAST-REPLY ::= CHOICE { + armored-data [0] KrbFastArmoredRep, + ... + } + + KrbFastArmoredRep ::= SEQUENCE { + enc-fast-rep [0] EncryptedData, -- KrbFastResponse -- + -- The encryption key is the armor key in the request, and + -- the key usage number is KEY_USAGE_FAST_REP. + ... + } + KEY_USAGE_FAST_REP 52 + + + +Hartman & Zhu Standards Track [Page 30] + +RFC 6113 Kerberos Preauth Framework April 2011 + + + The PA-FX-FAST-REPLY structure contains a KrbFastArmoredRep + structure. The KrbFastArmoredRep structure encapsulates the padata + in the KDC reply in the encrypted form. The KrbFastResponse is + encrypted with the armor key used in the corresponding request, and + the key usage number is KEY_USAGE_FAST_REP. + + The Kerberos client MUST support a local policy that rejects the + response if PA-FX-FAST-REPLY is not included in the response. + Clients MAY also support policies that fall back to other mechanisms + or that do not use pre-authentication when FAST is unavailable. It + is important to consider the potential downgrade attacks when + deploying such a policy. + + The KrbFastResponse structure contains the following information: + + KrbFastResponse ::= SEQUENCE { + padata [0] SEQUENCE OF PA-DATA, + -- padata typed holes. + strengthen-key [1] EncryptionKey OPTIONAL, + -- This, if present, strengthens the reply key for AS and + -- TGS. MUST be present for TGS. + -- MUST be absent in KRB-ERROR. + finished [2] KrbFastFinished OPTIONAL, + -- Present in AS or TGS reply; absent otherwise. + nonce [3] UInt32, + -- Nonce from the client request. + ... + } + + The padata field in the KrbFastResponse structure contains a list of + PA-DATA structures as described in Section 5.2.7 of [RFC4120]. These + PA-DATA structures are used to carry data advancing the exchange + specific for the FAST factors. They can also be used as generic + typed-holes for protocol extensibility. Unless otherwise specified, + the KDC MUST include any padata that are otherwise in the outer KDC- + REP or KDC-ERROR structure into this field. The padata field in the + KDC reply structure outside of the PA-FX-FAST-REPLY structure + typically includes only the PA-FX-FAST-REPLY padata. + + The strengthen-key field provides a mechanism for the KDC to + strengthen the reply key. If set, the strengthen-key value MUST be + randomly generated to have the same etype as that of the reply key + before being strengthened, and then the reply key is strengthened + after all padata items are processed. Let padata-reply-key be the + reply key after padata processing. + + reply-key = KRB-FX-CF2(strengthen-key, padata-reply-key, + "strengthenkey", "replykey") + + + +Hartman & Zhu Standards Track [Page 31] + +RFC 6113 Kerberos Preauth Framework April 2011 + + + The strengthen-key field MAY be set in an AS reply; it MUST be set in + a TGS reply; it must be absent in an error reply. The strengthen key + is required in a TGS reply so that an attacker cannot remove the FAST + PADATA from a TGS reply, causing the KDC to appear not to support + FAST. + + The finished field contains a KrbFastFinished structure. It is + filled by the KDC in the final message in the conversation. This + field is present in an AS-REP or a TGS-REP when a ticket is returned, + and it is not present in an error reply. + + The KrbFastFinished structure contains the following information: + + KrbFastFinished ::= SEQUENCE { + timestamp [0] KerberosTime, + usec [1] Microseconds, + -- timestamp and usec represent the time on the KDC when + -- the reply was generated. + crealm [2] Realm, + cname [3] PrincipalName, + -- Contains the client realm and the client name. + ticket-checksum [4] Checksum, + -- checksum of the ticket in the KDC-REP using the armor + -- and the key usage is KEY_USAGE_FAST_FINISH. + -- The checksum type is the required checksum type + -- of the armor key. + ... + } + KEY_USAGE_FAST_FINISHED 53 + + The timestamp and usec fields represent the time on the KDC when the + reply ticket was generated, these fields have the same semantics as + the corresponding identically named fields in Section 5.6.1 of + [RFC4120]. The client MUST use the KDC's time in these fields + thereafter when using the returned ticket. The client need not + confirm that the timestamp returned is within allowable clock skew: + the armor key guarantees that the reply is fresh. The client MAY + trust the timestamp returned. + + The cname and crealm fields identify the authenticated client. If + facilities described in [REFERRALS] are used, the authenticated + client may differ from the client in the FAST request. + + The ticket-checksum is a checksum of the issued ticket. The checksum + key is the armor key, and the checksum type is the required checksum + type of the enctype of that key, and the key usage number is + KEY_USAGE_FAST_FINISHED. + + + + +Hartman & Zhu Standards Track [Page 32] + +RFC 6113 Kerberos Preauth Framework April 2011 + + + When FAST padata is included, the PA-FX-COOKIE padata as defined in + Section 5.2 MUST be included in the padata sequence in the + KrbFastResponse sequence if the KDC expects at least one more message + from the client in order to complete the authentication. + + The nonce field in the KrbFastResponse contains the value of the + nonce field in the KDC-REQ of the corresponding client request and it + binds the KDC response with the client request. The client MUST + verify that this nonce value in the reply matches with that of the + request and reject the KDC reply otherwise. To prevent the response + from one message in a conversation from being replayed to a request + in another message, clients SHOULD use a new nonce for each message + in a conversation. + +5.4.4. Authenticated Kerberos Error Messages Using Kerberos FAST + + If the Kerberos FAST padata was included in the request, unless + otherwise specified, the e-data field of the KRB-ERROR message + [RFC4120] contains the ASN.1 DER encoding of the type METHOD-DATA + [RFC4120] and a PA-FX-FAST is included in the METHOD-DATA. The KDC + MUST include all the padata elements such as PA-ETYPE-INFO2 and + padata elements that indicate acceptable pre-authentication + mechanisms [RFC4120] in the KrbFastResponse structure. + + The KDC MUST also include a PA-FX-ERROR padata item in the + KRBFastResponse structure. The padata-value element of this sequence + is the ASN.1 DER encoding of the type KRB-ERROR. The e-data field + MUST be absent in the PA-FX-ERROR padata. All other fields should be + the same as the outer KRB-ERROR. The client ignores the outer error + and uses the combination of the padata in the KRBFastResponse and the + error information in the PA-FX-ERROR. + + PA-FX-ERROR 137 + + If the Kerberos FAST padata is included in the request but not + included in the error reply, it is a matter of the local policy on + the client to accept the information in the error message without + integrity protection. However, the client SHOULD process the KDC + errors as the result of the KDC's inability to accept the AP_REQ + armor and potentially retry another request with a different armor + when applicable. The Kerberos client MAY process an error message + without a PA-FX-FAST-REPLY, if that is only intended to return better + error information to the application, typically for trouble-shooting + purposes. + + In the cases where the e-data field of the KRB-ERROR message is + expected to carry a TYPED-DATA [RFC4120] element, that information + should be transmitted in a pa-data element within the KRBFastResponse + + + +Hartman & Zhu Standards Track [Page 33] + +RFC 6113 Kerberos Preauth Framework April 2011 + + + structure. The padata-type is the same as the data-type would be in + the typed data element and the padata-value is the same as the data- + value. As discussed in Section 7, data-types and padata-types are + drawn from the same namespace. For example, the + TD_TRUSTED_CERTIFIERS structure is expected to be in the KRB-ERROR + message when the error code is KDC_ERR_CANT_VERIFY_CERTIFICATE + [RFC4556]. + +5.4.5. Outer and Inner Requests + + Typically, a client will know that FAST is being used before a + request containing PA-FX-FAST is sent. So, the outer AS request + typically only includes one pa-data item: PA-FX-FAST. The client MAY + include additional pa-data, but the KDC MUST ignore the outer request + body and any padata besides PA-FX-FAST if and only if PA-FX-FAST is + processed. In the case of the TGS request, the outer request should + include PA-FX-FAST and PA-TGS-REQ. + + When an AS generates a response, all padata besides PA-FX-FAST should + be included in PA-FX-FAST. The client MUST ignore other padata + outside of PA-FX-FAST. + +5.4.6. The Encrypted Challenge FAST Factor + + The encrypted challenge FAST factor authenticates a client using the + client's long-term key. This factor works similarly to the encrypted + timestamp pre-authentication option described in [RFC4120]. The word + "challenge" is used instead of "timestamp" because while the + timestamp is used as an initial challenge, if the KDC and client do + not have synchronized time, then the KDC can provide updated time to + the client to use as a challenge. The client encrypts a structure + containing a timestamp in the challenge key. The challenge key used + by the client to send a message to the KDC is KRB-FX- + CF2(armor_key,long_term_key, "clientchallengearmor", + "challengelongterm"). The challenge key used by the KDC encrypting + to the client is KRB-FX-CF2(armor_key, long_term_key, + "kdcchallengearmor", "challengelongterm"). Because the armor key is + fresh and random, the challenge key is fresh and random. The only + purpose of the timestamp is to limit the validity of the + authentication so that a request cannot be replayed. A client MAY + base the timestamp on the KDC time in a KDC error and need not + maintain accurate time synchronization itself. If a client bases its + time on an untrusted source, an attacker may trick the client into + producing an authentication request that is valid at some future + time. The attacker may be able to use this authentication request to + make it appear that a client has authenticated at that future time. + If ticket-based armor is used, then the lifetime of the ticket will + limit the window in which an attacker can make the client appear to + + + +Hartman & Zhu Standards Track [Page 34] + +RFC 6113 Kerberos Preauth Framework April 2011 + + + have authenticated. For many situations, the ability of an attacker + to cause a client to appear to have authenticated is not a + significant concern; the ability to avoid requiring time + synchronization on clients is more valuable. + + The client sends a padata of type PA-ENCRYPTED-CHALLENGE. The + corresponding padata-value contains the DER encoding of ASN.1 type + EncryptedChallenge. + + EncryptedChallenge ::= EncryptedData + -- Encrypted PA-ENC-TS-ENC, encrypted in the challenge key + -- using key usage KEY_USAGE_ENC_CHALLENGE_CLIENT for the + -- client and KEY_USAGE_ENC_CHALLENGE_KDC for the KDC. + + PA-ENCRYPTED-CHALLENGE 138 + KEY_USAGE_ENC_CHALLENGE_CLIENT 54 + KEY_USAGE_ENC_CHALLENGE_KDC 55 + + The client includes some timestamp reasonably close to the KDC's + current time and encrypts it in the challenge key in a PA-ENC-TS-ENC + structure (see Section 5.2.7.2 in RFC 4120). Clients MAY use the + current time; doing so prevents the exposure where an attacker can + cause a client to appear to authenticate in the future. The client + sends the request including this factor. + + On receiving an AS-REQ containing the PA-ENCRYPTED-CHALLENGE FAST + factor, the KDC decrypts the timestamp. If the decryption fails the + KDC SHOULD return KDC_ERR_PREAUTH_FAILED, including PA-ETYPE-INFO2 in + the KRBFastResponse in the error. The KDC confirms that the + timestamp falls within its current clock skew returning + KRB_APP_ERR_SKEW if not. The KDC then SHOULD check to see if the + encrypted challenge is a replay. The KDC MUST NOT consider two + encrypted challenges replays simply because the timestamps are the + same; to be a replay, the ciphertext MUST be identical. Allowing + clients to reuse timestamps avoids requiring that clients maintain + state about which timestamps have been used. + + If the KDC accepts the encrypted challenge, it MUST include a padata + element of type PA-ENCRYPTED-CHALLENGE. The KDC encrypts its current + time in the challenge key. The KDC MUST strengthen the reply key + before issuing a ticket. The client MUST check that the timestamp + decrypts properly. The client MAY check that the timestamp is within + the window of acceptable clock skew for the client. The client MUST + NOT require that the timestamp be identical to the timestamp in the + issued credentials or the returned message. + + + + + + +Hartman & Zhu Standards Track [Page 35] + +RFC 6113 Kerberos Preauth Framework April 2011 + + + The encrypted challenge FAST factor provides the following + facilities: Client Authentication and KDC Authentication. This FAST + factor also takes advantage of the FAST facility to strengthen the + reply key. It does not provide the Replace Reply Key facility. The + Security Considerations section of this document provides an + explanation why the security requirements are met. + + The encrypted challenge FAST factor can be useful in an + authentication set. No pa-hint is defined because the only + information needed by this mechanism is information contained in the + PA-ETYPE-INFO2 pre-authentication data. KDCs are already required to + send PA-ETYPE-INFO2. If KDCs were not required to send PA-ETYPE- + INFO2 then that information would need to be part of a hint for + encrypted challenge. + + Conforming implementations MUST support the encrypted challenge FAST + factor. + +5.5. Authentication Strength Indication + + Implementations that have pre-authentication mechanisms offering + significantly different strengths of client authentication MAY choose + to keep track of the strength of the authentication used as an input + into policy decisions. For example, some principals might require + strong pre-authentication, while less sensitive principals can use + relatively weak forms of pre-authentication like encrypted timestamp. + + An AuthorizationData data type AD-Authentication-Strength is defined + for this purpose. + + AD-authentication-strength 70 + + The corresponding ad-data field contains the DER encoding of the pre- + authentication data set as defined in Section 5.3. This set contains + all the pre-authentication mechanisms that were used to authenticate + the client. If only one pre-authentication mechanism was used to + authenticate the client, the pre-authentication set contains one + element. Unless otherwise specified, the hint and value fields of + the members of this sequence MUST be empty. In order to permit + mechanisms to carry additional information about strength in these + fields in the future, clients and application servers MUST ignore + non-empty hint and value fields for mechanisms unless the + implementation is updated with the interpretation of these fields for + a given pre-authentication mechanism in this authorization element. + + The AD-authentication-strength element MUST be included in the AD- + KDC-ISSUED container so that the KDC integrity protects its contents. + This element can be ignored if it is unknown to the receiver. + + + +Hartman & Zhu Standards Track [Page 36] + +RFC 6113 Kerberos Preauth Framework April 2011 + + +6. Assigned Constants + + The pre-authentication framework and FAST involve using a number of + Kerberos protocol constants. This section lists protocol constants + first introduced in this specification drawn from registries not + managed by IANA. Many of these registries would best be managed by + IANA; that is a known issue that is out of scope for this document. + The constants described in this section have been accounted for and + will appear in the next revision of the Kerberos core specification + or in a document creating IANA registries. + + Section 7 creates IANA registries for a different set of constants + used by the extensions described in this document. + +6.1. New Errors + + KDC_ERR_PREAUTH_EXPIRED 90 + KDC_ERR_MORE_PREAUTH_DATA_REQUIRED 91 + KDC_ERR_PREAUTH_BAD_AUTHENTICATION_SET 92 + KDC_ERR_UNKNOWN_CRITICAL_FAST_OPTIONS 93 + +6.2. Key Usage Numbers + + KEY_USAGE_FAST_REQ_CHKSUM 50 + KEY_USAGE_FAST_ENC 51 + KEY_USAGE_FAST_REP 52 + KEY_USAGE_FAST_FINISHED 53 + KEY_USAGE_ENC_CHALLENGE_CLIENT 54 + KEY_USAGE_ENC_CHALLENGE_KDC 55 + +6.3. Authorization Data Elements + + AD-authentication-strength 70 + AD-fx-fast-armor 71 + AD-fx-fast-used 72 + +6.4. New PA-DATA Types + + PA-FX-COOKIE 133 + PA-AUTHENTICATION-SET 134 + PA-AUTH-SET-SELECTED 135 + PA-FX-FAST 136 + PA-FX-ERROR 137 + PA-ENCRYPTED-CHALLENGE 138 + + + + + + + +Hartman & Zhu Standards Track [Page 37] + +RFC 6113 Kerberos Preauth Framework April 2011 + + +7. IANA Considerations + + This document creates a number of IANA registries. These registries + are all located under Kerberos Parameters on http://www.iana.org. + See [RFC5226] for descriptions of the registration policies used in + this section. + +7.1. Pre-Authentication and Typed Data + + RFC 4120 defines pre-authentication data, which can be included in a + KDC request or response in order to authenticate the client or extend + the protocol. In addition, it defines Typed-Data, which is an + extension mechanism for errors. Both pre-authentication data and + typed data are carried as a 32-bit signed integer along with an octet + string. The encoding of typed data and pre-authentication data is + slightly different. However, the types for pre-authentication data + and typed-data are drawn from the same namespace. By convention, + registrations starting with TD- are typed data and registrations + starting with PA- are pre-authentication data. It is important that + these data types be drawn from the same namespace, because some + errors where it would be desirable to include typed data require the + e-data field to be formatted as pre-authentication data. + + When Kerberos FAST is used, pre-authentication data encoding is + always used. + + There is one apparently conflicting registration between typed data + and pre-authentication data. PA-GET-FROM-TYPED-DATA and TD-PADATA + are both assigned the value 22. However, this registration is simply + a mechanism to include an element of the other encoding. The use of + both should be deprecated. + + This document creates a registry for pre-authentication and typed + data. The registration procedures are as follows. Expert review for + pre-authentication mechanisms designed to authenticate users, KDCs, + or establish the reply key. The expert first determines that the + purpose of the method is to authenticate clients, KDCs, or to + establish the reply key. If so, expert review is appropriate. The + expert evaluates the security and interoperability of the + specification. + + IETF review is required if the expert believes that the pre- + authentication method is broader than these three areas. Pre- + authentication methods that change the Kerberos state machine or + otherwise make significant changes to the Kerberos protocol should be + Standards Track RFCs. A concern that a particular method needs to be + a Standards Track RFC may be raised as an objection during IETF + review. + + + +Hartman & Zhu Standards Track [Page 38] + +RFC 6113 Kerberos Preauth Framework April 2011 + + + Several of the registrations indicated below were made at a time when + the Kerberos protocol was less mature and do not meet the current + requirements for this registry. These registrations are included in + order to accurately document what is known about the use of these + protocol code points and to avoid conflicts. + + Type Value Reference + ---------------------------------------------------------------------- + PA-TGS-REQ 1 [RFC4120] + PA-ENC-TIMESTAMP 2 [RFC4120] + PA-PW-SALT 3 [RFC4120] + [reserved] 4 [RFC6113] + PA-ENC-UNIX-TIME 5 (deprecated) [RFC4120] + PA-SANDIA-SECUREID 6 [RFC4120] + PA-SESAME 7 [RFC4120] + PA-OSF-DCE 8 [RFC4120] + PA-CYBERSAFE-SECUREID 9 [RFC4120] + PA-AFS3-SALT 10 [RFC4120] [RFC3961] + PA-ETYPE-INFO 11 [RFC4120] + PA-SAM-CHALLENGE 12 [KRB-WG.SAM] + PA-SAM-RESPONSE 13 [KRB-WG.SAM] + PA-PK-AS-REQ_OLD 14 [PK-INIT-1999] + PA-PK-AS-REP_OLD 15 [PK-INIT-1999] + PA-PK-AS-REQ 16 [RFC4556] + PA-PK-AS-REP 17 [RFC4556] + PA-PK-OCSP-RESPONSE 18 [RFC4557] + PA-ETYPE-INFO2 19 [RFC4120] + PA-USE-SPECIFIED-KVNO 20 [RFC4120] + PA-SVR-REFERRAL-INFO 20 [REFERRALS] + PA-SAM-REDIRECT 21 [KRB-WG.SAM] + PA-GET-FROM-TYPED-DATA 22 (embedded in typed data) [RFC4120] + TD-PADATA 22 (embeds padata) [RFC4120] + PA-SAM-ETYPE-INFO 23 (sam/otp) [KRB-WG.SAM] + PA-ALT-PRINC 24 (crawdad@fnal.gov) [HW-AUTH] + PA-SERVER-REFERRAL 25 [REFERRALS] + PA-SAM-CHALLENGE2 30 (kenh@pobox.com) [KRB-WG.SAM] + PA-SAM-RESPONSE2 31 (kenh@pobox.com) [KRB-WG.SAM] + PA-EXTRA-TGT 41 Reserved extra TGT [RFC6113] + TD-PKINIT-CMS-CERTIFICATES 101 CertificateSet from CMS + TD-KRB-PRINCIPAL 102 PrincipalName + TD-KRB-REALM 103 Realm + TD-TRUSTED-CERTIFIERS 104 [RFC4556] + TD-CERTIFICATE-INDEX 105 [RFC4556] + TD-APP-DEFINED-ERROR 106 Application specific [RFC6113] + TD-REQ-NONCE 107 INTEGER [RFC6113] + TD-REQ-SEQ 108 INTEGER [RFC6113] + TD_DH_PARAMETERS 109 [RFC4556] + TD-CMS-DIGEST-ALGORITHMS 111 [ALG-AGILITY] + + + +Hartman & Zhu Standards Track [Page 39] + +RFC 6113 Kerberos Preauth Framework April 2011 + + + TD-CERT-DIGEST-ALGORITHMS 112 [ALG-AGILITY] + PA-PAC-REQUEST 128 [MS-KILE] + PA-FOR_USER 129 [MS-KILE] + PA-FOR-X509-USER 130 [MS-KILE] + PA-FOR-CHECK_DUPS 131 [MS-KILE] + PA-AS-CHECKSUM 132 [MS-KILE] + PA-FX-COOKIE 133 [RFC6113] + PA-AUTHENTICATION-SET 134 [RFC6113] + PA-AUTH-SET-SELECTED 135 [RFC6113] + PA-FX-FAST 136 [RFC6113] + PA-FX-ERROR 137 [RFC6113] + PA-ENCRYPTED-CHALLENGE 138 [RFC6113] + PA-OTP-CHALLENGE 141 (gareth.richards@rsa.com) [OTP-PREAUTH] + PA-OTP-REQUEST 142 (gareth.richards@rsa.com) [OTP-PREAUTH] + PA-OTP-CONFIRM 143 (gareth.richards@rsa.com) [OTP-PREAUTH] + PA-OTP-PIN-CHANGE 144 (gareth.richards@rsa.com) [OTP-PREAUTH] + PA-EPAK-AS-REQ 145 (sshock@gmail.com) [RFC6113] + PA-EPAK-AS-REP 146 (sshock@gmail.com) [RFC6113] + PA_PKINIT_KX 147 [RFC6112] + PA_PKU2U_NAME 148 [PKU2U] + PA-SUPPORTED-ETYPES 165 [MS-KILE] + PA-EXTENDED_ERROR 166 [MS-KILE] + +7.2. Fast Armor Types + + FAST armor types are defined in Section 5.4.1. A FAST armor type is + a signed 32-bit integer. FAST armor types are assigned by standards + action. + + Type Name Description + ------------------------------------------------------------ + 0 Reserved. + 1 FX_FAST_ARMOR_AP_REQUEST Ticket armor using an ap-req. + +7.3. FAST Options + + A FAST request includes a set of bit flags to indicate additional + options. Bits 0-15 are critical; other bits are non-critical. + Assigning bits greater than 31 may require special support in + implementations. Assignment of FAST options requires standards + action. + + + + + + + + + + +Hartman & Zhu Standards Track [Page 40] + +RFC 6113 Kerberos Preauth Framework April 2011 + + + Type Name Description + ------------------------------------------------------------------- + 0 RESERVED Reserved for future expansion of this + field. + 1 hide-client-names Requesting the KDC to hide client + names in the KDC response + 16 kdc-follow-referrals Reserved. + +8. Security Considerations + + The kdc-referrals option in the Kerberos FAST padata requests the KDC + to act as the client to follow referrals. This can overload the KDC. + To limit the damages of denial of service using this option, KDCs MAY + restrict the number of simultaneous active requests with this option + for any given client principal. + + Regarding the facilities provided by the Encrypted Challenge FAST + factor, the challenge key is derived from the client secrets and + because the client secrets are known only to the client and the KDC, + the verification of the EncryptedChallenge structure proves the + client's identity, the verification of the EncryptedChallenge + structure in the KDC reply proves that the expected KDC responded. + Therefore, the Encrypted Challenge FAST factor as a pre- + authentication mechanism offers the following facilities: Client + Authentication and KDC Authentication. There is no un-authenticated + cleartext introduced by the Encrypted Challenge FAST factor. + + FAST provides an encrypted tunnel over which pre-authentication + conversations can take place. In addition, FAST optionally + authenticates the KDC to the client. It is the responsibility of + FAST factors to authenticate the client to the KDC. Care MUST be + taken to design FAST factors such that they are bound to the + conversation. If this is not done, a man-in-the-middle may be able + to cut&paste a FAST factor from one conversation to another. The + easiest way to do this is to bind each FAST factor to the armor key + that is guaranteed to be unique for each conversation. + + The anonymous PKINIT mode for obtaining an armor ticket does not + always authenticate the KDC to the client before the conversation + begins. Tracking the KDC verified state guarantees that by the end + of the conversation, the client has authenticated the KDC. However, + FAST factor designers need to consider the implications of using + their factor when the KDC has not yet been authenticated. If this + proves problematic in an environment, then the particular FAST factor + should not be used with anonymous PKINIT. + + Existing pre-authentication mechanisms are believed to be at least as + secure when used with FAST as they are when used outside of FAST. + + + +Hartman & Zhu Standards Track [Page 41] + +RFC 6113 Kerberos Preauth Framework April 2011 + + + One part of this security is making sure that when pre-authentication + methods checksum the request, they checksum the inner request rather + than the outer request. If the mechanism checksummed the outer + request, a man-in-the-middle could observe it outside a FAST tunnel + and then cut&paste it into a FAST exchange where the inner rather + than outer request would be used to select attributes of the issued + ticket. Such attacks would typically invalidate auditing information + or create a situation where the client and KDC disagree about what + ticket is issued. However, such attacks are unlikely to allow an + attacker who would not be able to authenticate as a principal to do + so. Even so, FAST is believed to defend against these attacks in + existing legacy mechanism. However, since there is no standard for + how legacy mechanisms bind the request to the pre-authentication or + provide integrity protection, security analysis can be difficult. In + some cases, FAST may significantly improve the integrity protection + of legacy mechanisms. + + The security of the TGS exchange depends on authenticating the client + to the KDC. In the AS exchange, this is done using pre- + authentication data or FAST factors. In the TGS exchange, this is + done by presenting a TGT and by using the session (or sub-session) + key in constructing the request. Because FAST uses a request body in + the inner request, encrypted in the armor key, rather than the + request body in the outer request, it is critical that establishing + the armor key be tied to the authentication of the client to the KDC. + If this is not done, an attacker could manipulate the options + requested in the TGS request, for example, requesting a ticket with + different validity or addresses. The easiest way to bind the armor + key to the authentication of the client to the KDC is for the armor + key to depend on the sub-session key of the TGT. This is done with + the implicit TGS armor supported by this specification. Future armor + types designed for use with the TGS MUST either bind their armor keys + to the TGT or provide another mechanism to authenticate the client to + the KDC. + +9. Acknowledgements + + Sam Hartman would like to thank the MIT Kerberos Consortium for its + funding of his time on this project. + + Several suggestions from Jeffrey Hutzelman based on early revisions + of this documents led to significant improvements of this document. + + The proposal to ask one KDC to chase down the referrals and return + the final ticket is based on requirements in [CROSS]. + + Joel Weber had a proposal for a mechanism similar to FAST that + created a protected tunnel for Kerberos pre-authentication. + + + +Hartman & Zhu Standards Track [Page 42] + +RFC 6113 Kerberos Preauth Framework April 2011 + + + Srinivas Cheruku and Greg Hudson provided valuable review comments. + +10. References + +10.1. Normative References + + [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate + Requirement Levels", BCP 14, RFC 2119, March 1997. + + [RFC3961] Raeburn, K., "Encryption and Checksum Specifications + for Kerberos 5", RFC 3961, February 2005. + + [RFC4120] Neuman, C., Yu, T., Hartman, S., and K. Raeburn, "The + Kerberos Network Authentication Service (V5)", + RFC 4120, July 2005. + + [RFC4556] Zhu, L. and B. Tung, "Public Key Cryptography for + Initial Authentication in Kerberos (PKINIT)", + RFC 4556, June 2006. + + [RFC5226] Narten, T. and H. Alvestrand, "Guidelines for Writing + an IANA Considerations Section in RFCs", BCP 26, + RFC 5226, May 2008. + + [RFC6112] Zhu, L., Leach, P., and S. Hartman "Anonymity Support + for Kerberos", RFC 6112, April 2011. + +10.2. Informative References + + [ALG-AGILITY] Astrand, L. and L. Zhu, "PK-INIT algorithm agility", + Work in Progress, August 2008. + + [CROSS] Sakane, S., Zrelli, S., and M. Ishiyama , "Problem + statement on the cross-realm operation of Kerberos in + a specific system", Work in Progress, July 2007. + + [EKE] Bellovin, S. and M. Merritt, "Augmented Encrypted Key + Exchange: A Password-Based Protocol Secure Against + Dictionary Attacks and Password File Compromise, + Proceedings of the 1st ACM Conference on Computer and + Communications Security, ACM Press.", November 1993. + + [HW-AUTH] Crawford, M., "Passwordless Initial Authentication to + Kerberos by Hardware Preauthentication", Work + in Progress, October 2006. + + [IEEE1363.2] IEEE, "IEEE P1363.2: Password-Based Public-Key + Cryptography", 2004. + + + +Hartman & Zhu Standards Track [Page 43] + +RFC 6113 Kerberos Preauth Framework April 2011 + + + [KRB-WG.SAM] Hornstein, K., Renard, K., Neuman, C., and G. Zorn, + "Integrating Single-use Authentication Mechanisms + with Kerberos", Work in Progress, July 2004. + + [MS-KILE] Microsoft, "Kerberos Protocol Extensions", . + + [OTP-PREAUTH] Richards, G., "OTP Pre-authentication", Work + in Progress, February 2011. + + [PK-INIT-1999] Tung, B., Neuman, C., Hur, M., Medvinsky, A., + Medvinsky, S., Wray, J., and J. Trostle, "Public Key + Cryptography for Initial Authentication in Kerberos", + Work in Progress, July 1999. + + [PKU2U] Zhu, L., Altman, J., and N. Williams, "Public Key + Cryptography Based User-to-User Authentication - + (PKU2U)", Work in Progress, November 2008. + + [REFERRALS] Hartman, S., Ed., Raeburn, K., and L. Zhu, "Kerberos + Principal Name Canonicalization and KDC-Generated + Cross-Realm Referrals", Work in Progress, March 2011. + + [RFC4557] Zhu, L., Jaganathan, K., and N. Williams, "Online + Certificate Status Protocol (OCSP) Support for Public + Key Cryptography for Initial Authentication in + Kerberos (PKINIT)", RFC 4557, June 2006. + + + + + + + + + + + + + + + + + + + + + + + + +Hartman & Zhu Standards Track [Page 44] + +RFC 6113 Kerberos Preauth Framework April 2011 + + +Appendix A. Test Vectors for KRB-FX-CF2 + + This informative appendix presents test vectors for the KRB-FX-CF2 + function. Test vectors are presented for several encryption types. + In all cases, the first key (k1) is the result of string-to- + key("key1", "key1", default_parameters) and the second key (k2) is + the result of string-to-key("key2", "key2", default_parameters). + Both keys are of the same enctype. The presented test vector is the + hexadecimal encoding of the key produced by KRB-FX-CF2(k1, k2, "a", + "b"). The peppers are one-octet ASCII strings. + + In performing interoperability testing, there was significant + ambiguity surrounding [RFC3961] pseudo-random operations. These test + vectors assume that the AES pseudo-random operation is + aes-ecb(trunc128(sha-1(input))) where trunc128 truncates its input to + 128 bits. The 3DES pseudo-random operation is assumed to be + des3-cbc(trunc128(sha-1(input))). The DES pseudo-random operation is + assumed to be des-cbc(md5(input)). As specified in RFC 4757, the RC4 + pseudo-random operation is hmac-sha1(input). + + Interoperability testing also demonstrated ambiguity surrounding the + DES random-to-key operation. The random-to-key operation is assumed + to be distribute 56 bits into high-7-bits of 8 octets and generate + parity. + + These test vectors were produced with revision 22359 of the MIT + Kerberos sources. The AES 256 and AES 128 test vectors have been + confirmed by multiple other implementors. The RC4 test vectors have + been confirmed by one other implementor. The DES and triple DES test + vectors have not been confirmed. + + aes 128 (enctype 17): 97df97e4b798b29eb31ed7280287a92a + AES256 (enctype 18): 4d6ca4e629785c1f01baf55e2e548566 + b9617ae3a96868c337cb93b5e72b1c7b + DES (enctype 1): 43bae3738c9467e6 + 3DES (enctype 16): e58f9eb643862c13ad38e529313462a7f73e62834fe54a01 + RC4 (enctype 23): 24d7f6b6bae4e5c00d2082c5ebab3672 + + + + + + + + + + + + + + +Hartman & Zhu Standards Track [Page 45] + +RFC 6113 Kerberos Preauth Framework April 2011 + + +Appendix B. ASN.1 Module + + KerberosPreauthFramework { + iso(1) identified-organization(3) dod(6) internet(1) + security(5) kerberosV5(2) modules(4) preauth-framework(3) + } DEFINITIONS EXPLICIT TAGS ::= BEGIN + + IMPORTS + KerberosTime, PrincipalName, Realm, EncryptionKey, Checksum, + Int32, EncryptedData, PA-ENC-TS-ENC, PA-DATA, KDC-REQ-BODY, + Microseconds, KerberosFlags, UInt32 + FROM KerberosV5Spec2 { iso(1) identified-organization(3) + dod(6) internet(1) security(5) kerberosV5(2) + modules(4) krb5spec2(2) }; + -- as defined in RFC 4120. + + PA-AUTHENTICATION-SET ::= SEQUENCE OF PA-AUTHENTICATION-SET-ELEM + + PA-AUTHENTICATION-SET-ELEM ::= SEQUENCE { + pa-type [0] Int32, + -- same as padata-type. + pa-hint [1] OCTET STRING OPTIONAL, + pa-value [2] OCTET STRING OPTIONAL, + ... + } + + KrbFastArmor ::= SEQUENCE { + armor-type [0] Int32, + -- Type of the armor. + armor-value [1] OCTET STRING, + -- Value of the armor. + ... + } + + PA-FX-FAST-REQUEST ::= CHOICE { + armored-data [0] KrbFastArmoredReq, + ... + } + + KrbFastArmoredReq ::= SEQUENCE { + armor [0] KrbFastArmor OPTIONAL, + -- Contains the armor that identifies the armor key. + -- MUST be present in AS-REQ. + req-checksum [1] Checksum, + -- For AS, contains the checksum performed over the type + -- KDC-REQ-BODY for the req-body field of the KDC-REQ + -- structure; + -- For TGS, contains the checksum performed over the type + + + +Hartman & Zhu Standards Track [Page 46] + +RFC 6113 Kerberos Preauth Framework April 2011 + + + -- AP-REQ in the PA-TGS-REQ padata. + -- The checksum key is the armor key, the checksum + -- type is the required checksum type for the enctype of + -- the armor key, and the key usage number is + -- KEY_USAGE_FAST_REQ_CHKSUM. + enc-fast-req [2] EncryptedData, -- KrbFastReq -- + -- The encryption key is the armor key, and the key usage + -- number is KEY_USAGE_FAST_ENC. + ... + } + + KrbFastReq ::= SEQUENCE { + fast-options [0] FastOptions, + -- Additional options. + padata [1] SEQUENCE OF PA-DATA, + -- padata typed holes. + req-body [2] KDC-REQ-BODY, + -- Contains the KDC request body as defined in Section + -- 5.4.1 of [RFC4120]. + -- This req-body field is preferred over the outer field + -- in the KDC request. + ... + } + + FastOptions ::= KerberosFlags + -- reserved(0), + -- hide-client-names(1), + -- kdc-follow-referrals(16) + + PA-FX-FAST-REPLY ::= CHOICE { + armored-data [0] KrbFastArmoredRep, + ... + } + + KrbFastArmoredRep ::= SEQUENCE { + enc-fast-rep [0] EncryptedData, -- KrbFastResponse -- + -- The encryption key is the armor key in the request, and + -- the key usage number is KEY_USAGE_FAST_REP. + ... + } + + KrbFastResponse ::= SEQUENCE { + padata [0] SEQUENCE OF PA-DATA, + -- padata typed holes. + strengthen-key [1] EncryptionKey OPTIONAL, + -- This, if present, strengthens the reply key for AS and + -- TGS. MUST be present for TGS + -- MUST be absent in KRB-ERROR. + + + +Hartman & Zhu Standards Track [Page 47] + +RFC 6113 Kerberos Preauth Framework April 2011 + + + finished [2] KrbFastFinished OPTIONAL, + -- Present in AS or TGS reply; absent otherwise. + nonce [3] UInt32, + -- Nonce from the client request. + ... + } + + KrbFastFinished ::= SEQUENCE { + timestamp [0] KerberosTime, + usec [1] Microseconds, + -- timestamp and usec represent the time on the KDC when + -- the reply was generated. + crealm [2] Realm, + cname [3] PrincipalName, + -- Contains the client realm and the client name. + ticket-checksum [4] Checksum, + -- checksum of the ticket in the KDC-REP using the armor + -- and the key usage is KEY_USAGE_FAST_FINISH. + -- The checksum type is the required checksum type + -- of the armor key. + ... + } + + EncryptedChallenge ::= EncryptedData + -- Encrypted PA-ENC-TS-ENC, encrypted in the challenge key + -- using key usage KEY_USAGE_ENC_CHALLENGE_CLIENT for the + -- client and KEY_USAGE_ENC_CHALLENGE_KDC for the KDC. + END + +Authors' Addresses + + Sam Hartman + Painless Security + + EMail: hartmans-ietf@mit.edu + + + Larry Zhu + Microsoft Corporation + One Microsoft Way + Redmond, WA 98052 + US + + EMail: larry.zhu@microsoft.com + + + + + + + +Hartman & Zhu Standards Track [Page 48] + diff --git a/kadmin/init.c b/kadmin/init.c index 19f7328fc..ede740d4a 100644 --- a/kadmin/init.c +++ b/kadmin/init.c @@ -232,6 +232,15 @@ init(struct init_options *opt, int argc, char **argv) krb5_free_principal(context, princ); + /* Create `WELLKNONW/org.h5l.fast-cookie@WELLKNOWN:ORG.H5L' for FAST cookie */ + krb5_make_principal(context, &princ, KRB5_WELLKNOWN_ORG_H5L_REALM, + KRB5_WELLKNOWN_NAME, "org.h5l.fast-cookie", NULL); + create_random_entry(princ, 60*60, 60*60, + KRB5_KDB_REQUIRES_PRE_AUTH| + KRB5_KDB_DISALLOW_TGT_BASED| + KRB5_KDB_DISALLOW_ALL_TIX); + krb5_free_principal(context, princ); + /* Create `default' */ { kadm5_principal_ent_rec ent; diff --git a/kdc/Makefile.am b/kdc/Makefile.am index 2e14f3318..3b283cb03 100644 --- a/kdc/Makefile.am +++ b/kdc/Makefile.am @@ -37,6 +37,7 @@ libkdc_la_SOURCES = \ default_config.c \ set_dbinfo.c \ digest.c \ + fast.c \ kdc_locl.h \ kerberos5.c \ krb5tgs.c \ diff --git a/kdc/digest-service.c b/kdc/digest-service.c index 4d339a2dd..575850c29 100644 --- a/kdc/digest-service.c +++ b/kdc/digest-service.c @@ -44,6 +44,8 @@ typedef struct pk_client_params pk_client_params; struct DigestREQ; struct Kx509Request; +typedef struct kdc_request_desc *kdc_request_t; + #include krb5_kdc_configuration *config; diff --git a/kdc/fast.c b/kdc/fast.c new file mode 100644 index 000000000..38e96a0fc --- /dev/null +++ b/kdc/fast.c @@ -0,0 +1,557 @@ +/* + * Copyright (c) 1997-2011 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * Portions Copyright (c) 2010 - 2011 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "kdc_locl.h" + +static krb5_error_code +get_fastuser_crypto(kdc_request_t r, krb5_enctype enctype, krb5_crypto *crypto) +{ + krb5_principal fast_princ; + hdb_entry_ex *fast_user = NULL; + Key *cookie_key = NULL; + krb5_error_code ret; + + *crypto = NULL; + + ret = krb5_make_principal(r->context, &fast_princ, + KRB5_WELLKNOWN_ORG_H5L_REALM, + KRB5_WELLKNOWN_NAME, "org.h5l.fast-cookie", NULL); + if (ret) + goto out; + + ret = _kdc_db_fetch(r->context, r->config, fast_princ, + HDB_F_GET_CLIENT, NULL, NULL, &fast_user); + krb5_free_principal(r->context, fast_princ); + if (ret) + goto out; + + if (enctype == KRB5_ENCTYPE_NULL) + ret = _kdc_get_preferred_key(r->context, r->config, fast_user, + "fast-cookie", &enctype, &cookie_key); + else + ret = hdb_enctype2key(r->context, &fast_user->entry, + enctype, &cookie_key); + if (ret) + goto out; + + ret = krb5_crypto_init(r->context, &cookie_key->key, 0, crypto); + if (ret) + goto out; + + out: + if (fast_user) + _kdc_free_ent(r->context, fast_user); + + return ret; +} + + +static krb5_error_code +fast_parse_cookie(kdc_request_t r, const PA_DATA *pa) +{ + krb5_crypto crypto = NULL; + krb5_error_code ret; + KDCFastCookie data; + krb5_data d1; + size_t len; + + ret = decode_KDCFastCookie(pa->padata_value.data, + pa->padata_value.length, + &data, &len); + if (ret) + return ret; + + if (len != pa->padata_value.length || strcmp("H5L1", data.version) != 0) { + free_KDCFastCookie(&data); + return KRB5KDC_ERR_POLICY; + } + + ret = get_fastuser_crypto(r, data.cookie.etype, &crypto); + if (ret) + goto out; + + ret = krb5_decrypt_EncryptedData(r->context, crypto, + KRB5_KU_H5L_COOKIE, + &data.cookie, &d1); + krb5_crypto_destroy(r->context, crypto); + if (ret) + goto out; + + ret = decode_KDCFastState(d1.data, d1.length, &r->fast, &len); + krb5_data_free(&d1); + if (ret) + goto out; + + if (r->fast.expiration < kdc_time) { + kdc_log(r->context, r->config, 0, "fast cookie expired"); + ret = KRB5KDC_ERR_POLICY; + goto out; + } + + out: + free_KDCFastCookie(&data); + + return ret; +} + +static krb5_error_code +fast_add_cookie(kdc_request_t r, METHOD_DATA *method_data) +{ + krb5_crypto crypto = NULL; + KDCFastCookie shell; + krb5_error_code ret; + krb5_data data; + size_t size; + + memset(&shell, 0, sizeof(shell)); + + r->fast.expiration = kdc_time + FAST_EXPIRATION_TIME; + + ASN1_MALLOC_ENCODE(KDCFastState, data.data, data.length, + &r->fast, &size, ret); + if (ret) + return ret; + heim_assert(size == data.length, "internal asn1 encoder error"); + + ret = get_fastuser_crypto(r, KRB5_ENCTYPE_NULL, &crypto); + if (ret) + goto out; + + ret = krb5_encrypt_EncryptedData(r->context, crypto, + KRB5_KU_H5L_COOKIE, + data.data, data.length, 0, + &shell.cookie); + krb5_crypto_destroy(r->context, crypto); + if (ret) + goto out; + + free(data.data); + + shell.version = "H5L1"; + + ASN1_MALLOC_ENCODE(KDCFastCookie, data.data, data.length, + &shell, &size, ret); + free_EncryptedData(&shell.cookie); + if (ret) + goto out; + heim_assert(size == data.length, "internal asn1 encoder error"); + + ret = krb5_padata_add(r->context, method_data, + KRB5_PADATA_FX_COOKIE, + data.data, data.length); + out: + if (ret) + free(data.data); + return ret; +} + +krb5_error_code +_kdc_fast_mk_response(krb5_context context, + krb5_crypto armor_crypto, + METHOD_DATA *pa_data, + krb5_keyblock *strengthen_key, + KrbFastFinished *finished, + krb5uint32 nonce, + krb5_data *data) +{ + PA_FX_FAST_REPLY fxfastrep; + KrbFastResponse fastrep; + krb5_error_code ret; + krb5_data buf; + size_t size; + + memset(&fxfastrep, 0, sizeof(fxfastrep)); + memset(&fastrep, 0, sizeof(fastrep)); + krb5_data_zero(data); + + if (pa_data) { + fastrep.padata.val = pa_data->val; + fastrep.padata.len = pa_data->len; + } + fastrep.strengthen_key = strengthen_key; + fastrep.finished = finished; + fastrep.nonce = nonce; + + ASN1_MALLOC_ENCODE(KrbFastResponse, buf.data, buf.length, + &fastrep, &size, ret); + if (ret) + return ret; + if (buf.length != size) + krb5_abortx(context, "internal asn.1 error"); + + fxfastrep.element = choice_PA_FX_FAST_REPLY_armored_data; + + ret = krb5_encrypt_EncryptedData(context, + armor_crypto, + KRB5_KU_FAST_REP, + buf.data, + buf.length, + 0, + &fxfastrep.u.armored_data.enc_fast_rep); + krb5_data_free(&buf); + if (ret) + return ret; + + ASN1_MALLOC_ENCODE(PA_FX_FAST_REPLY, data->data, data->length, + &fxfastrep, &size, ret); + free_PA_FX_FAST_REPLY(&fxfastrep); + if (ret) + return ret; + if (data->length != size) + krb5_abortx(context, "internal asn.1 error"); + + return 0; +} + + +krb5_error_code +_kdc_fast_mk_error(krb5_context context, + kdc_request_t r, + METHOD_DATA *error_method, + krb5_crypto armor_crypto, + const KDC_REQ_BODY *req_body, + krb5_error_code outer_error, + const char *e_text, + krb5_principal error_client, + krb5_principal error_server, + time_t *csec, int *cusec, + krb5_data *error_msg) +{ + krb5_error_code ret; + krb5_data e_data; + size_t size; + + krb5_data_zero(&e_data); + + if (armor_crypto) { + PA_FX_FAST_REPLY fxfastrep; + KrbFastResponse fastrep; + + memset(&fxfastrep, 0, sizeof(fxfastrep)); + memset(&fastrep, 0, sizeof(fastrep)); + + /* first add the KRB-ERROR to the fast errors */ + + ret = krb5_mk_error(context, + outer_error, + e_text, + NULL, + error_client, + error_server, + NULL, + NULL, + &e_data); + if (ret) + return ret; + + ret = krb5_padata_add(context, error_method, + KRB5_PADATA_FX_ERROR, + e_data.data, e_data.length); + if (ret) { + krb5_data_free(&e_data); + return ret; + } + + if (/* hide_principal */ 0) { + error_client = NULL; + error_server = NULL; + e_text = NULL; + } + + if (r) + ret = fast_add_cookie(r, error_method); + else + ret = krb5_padata_add(context, error_method, + KRB5_PADATA_FX_COOKIE, + NULL, 0); + if (ret) { + kdc_log(r->context, r->config, 0, "failed to add fast cookie with: %d", ret); + free_METHOD_DATA(error_method); + return ret; + } + + ret = _kdc_fast_mk_response(context, armor_crypto, + error_method, NULL, NULL, + req_body->nonce, &e_data); + free_METHOD_DATA(error_method); + if (ret) + return ret; + + ret = krb5_padata_add(context, error_method, + KRB5_PADATA_FX_FAST, + e_data.data, e_data.length); + if (ret) + return ret; + } + + if (error_method && error_method->len) { + ASN1_MALLOC_ENCODE(METHOD_DATA, e_data.data, e_data.length, + error_method, &size, ret); + if (ret) + return ret; + if (e_data.length != size) + krb5_abortx(context, "internal asn.1 error"); + } + + ret = krb5_mk_error(context, + outer_error, + e_text, + (e_data.length ? &e_data : NULL), + error_client, + error_server, + csec, + cusec, + error_msg); + krb5_data_free(&e_data); + + return ret; +} + +krb5_error_code +_kdc_fast_unwrap_request(kdc_request_t r) +{ + krb5_principal armor_server = NULL; + hdb_entry_ex *armor_user = NULL; + PA_FX_FAST_REQUEST fxreq; + krb5_auth_context ac = NULL; + krb5_ticket *ticket = NULL; + krb5_flags ap_req_options; + Key *armor_key = NULL; + krb5_keyblock armorkey; + krb5_error_code ret; + krb5_ap_req ap_req; + unsigned char *buf; + KrbFastReq fastreq; + size_t len, size; + krb5_data data; + const PA_DATA *pa; + int i = 0; + + /* + * First look for FX_COOKIE and and process it + */ + pa = _kdc_find_padata(&r->req, &i, KRB5_PADATA_FX_COOKIE); + if (pa) { + ret = fast_parse_cookie(r, pa); + if (ret) + goto out; + } + + i = 0; + pa = _kdc_find_padata(&r->req, &i, KRB5_PADATA_FX_FAST); + if (pa == NULL) + return 0; + + ret = decode_PA_FX_FAST_REQUEST(pa->padata_value.data, + pa->padata_value.length, + &fxreq, + &len); + if (ret) + goto out; + if (len != pa->padata_value.length) { + ret = KRB5KDC_ERR_PREAUTH_FAILED; + goto out; + } + + if (fxreq.element != choice_PA_FX_FAST_REQUEST_armored_data) { + kdc_log(r->context, r->config, 0, + "AS-REQ FAST contain unknown type: %d", (int)fxreq.element); + ret = KRB5KDC_ERR_PREAUTH_FAILED; + goto out; + } + + /* pull out armor key */ + if (fxreq.u.armored_data.armor == NULL) { + kdc_log(r->context, r->config, 0, + "AS-REQ armor missing"); + ret = KRB5KDC_ERR_PREAUTH_FAILED; + goto out; + } + + if (fxreq.u.armored_data.armor->armor_type != 1) { + kdc_log(r->context, r->config, 0, + "AS-REQ armor type not ap-req"); + ret = KRB5KDC_ERR_PREAUTH_FAILED; + goto out; + } + + ret = krb5_decode_ap_req(r->context, + &fxreq.u.armored_data.armor->armor_value, + &ap_req); + if(ret) { + kdc_log(r->context, r->config, 0, "AP-REQ decode failed"); + goto out; + } + + /* Save that principal that was in the request */ + ret = _krb5_principalname2krb5_principal(r->context, + &armor_server, + ap_req.ticket.sname, + ap_req.ticket.realm); + if (ret) { + free_AP_REQ(&ap_req); + goto out; + } + + ret = _kdc_db_fetch(r->context, r->config, armor_server, + HDB_F_GET_SERVER, NULL, NULL, &armor_user); + if(ret == HDB_ERR_NOT_FOUND_HERE) { + kdc_log(r->context, r->config, 5, + "armor key does not have secrets at this KDC, " + "need to proxy"); + goto out; + } else if (ret) { + free_AP_REQ(&ap_req); + ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN; + goto out; + } + + ret = hdb_enctype2key(r->context, &armor_user->entry, + ap_req.ticket.enc_part.etype, + &armor_key); + if (ret) { + free_AP_REQ(&ap_req); + goto out; + } + + ret = krb5_verify_ap_req2(r->context, &ac, + &ap_req, + armor_server, + &armor_key->key, + 0, + &ap_req_options, + &ticket, + KRB5_KU_AP_REQ_AUTH); + free_AP_REQ(&ap_req); + if (ret) + goto out; + + if (ac->remote_subkey == NULL) { + krb5_auth_con_free(r->context, ac); + kdc_log(r->context, r->config, 0, + "FAST AP-REQ remote subkey missing"); + ret = KRB5KDC_ERR_PREAUTH_FAILED; + goto out; + } + + ret = _krb5_fast_armor_key(r->context, + ac->remote_subkey, + &ticket->ticket.key, + &armorkey, + &r->armor_crypto); + krb5_auth_con_free(r->context, ac); + krb5_free_ticket(r->context, ticket); + if (ret) + goto out; + + krb5_free_keyblock_contents(r->context, &armorkey); + + /* verify req-checksum of the outer body */ + + ASN1_MALLOC_ENCODE(KDC_REQ_BODY, buf, len, &r->req.req_body, &size, ret); + if (ret) + goto out; + if (size != len) { + ret = KRB5KDC_ERR_PREAUTH_FAILED; + goto out; + } + + ret = krb5_verify_checksum(r->context, r->armor_crypto, + KRB5_KU_FAST_REQ_CHKSUM, + buf, len, + &fxreq.u.armored_data.req_checksum); + free(buf); + if (ret) { + kdc_log(r->context, r->config, 0, + "FAST request have a bad checksum"); + goto out; + } + + ret = krb5_decrypt_EncryptedData(r->context, r->armor_crypto, + KRB5_KU_FAST_ENC, + &fxreq.u.armored_data.enc_fast_req, + &data); + if (ret) { + kdc_log(r->context, r->config, 0, + "Failed to decrypt FAST request"); + goto out; + } + + ret = decode_KrbFastReq(data.data, data.length, &fastreq, &size); + if (ret) { + krb5_data_free(&data); + goto out; + } + if (data.length != size) { + krb5_data_free(&data); + ret = KRB5KDC_ERR_PREAUTH_FAILED; + goto out; + } + krb5_data_free(&data); + + free_KDC_REQ_BODY(&r->req.req_body); + ret = copy_KDC_REQ_BODY(&fastreq.req_body, &r->req.req_body); + if (ret) + goto out; + + /* check for unsupported mandatory options */ + if (FastOptions2int(fastreq.fast_options) & 0xfffc) { + kdc_log(r->context, r->config, 0, + "FAST unsupported mandatory option set"); + ret = KRB5KDC_ERR_PREAUTH_FAILED; + goto out; + } + + /* KDC MUST ignore outer pa data preauth-14 - 6.5.5 */ + if (r->req.padata) + free_METHOD_DATA(r->req.padata); + else + ALLOC(r->req.padata); + + ret = copy_METHOD_DATA(&fastreq.padata, r->req.padata); + if (ret) + goto out; + + free_KrbFastReq(&fastreq); + free_PA_FX_FAST_REQUEST(&fxreq); + + out: + if (armor_server) + krb5_free_principal(r->context, armor_server); + if(armor_user) + _kdc_free_ent(r->context, armor_user); + + return ret; +} diff --git a/kdc/headers.h b/kdc/headers.h index aced5ce61..9e7af82e9 100644 --- a/kdc/headers.h +++ b/kdc/headers.h @@ -106,6 +106,8 @@ #include #include +#include + #undef ALLOC #define ALLOC(X) ((X) = calloc(1, sizeof(*(X)))) #undef ALLOC_SEQ diff --git a/kdc/kdc_locl.h b/kdc/kdc_locl.h index 36d694dae..9d3c73a32 100644 --- a/kdc/kdc_locl.h +++ b/kdc/kdc_locl.h @@ -43,8 +43,53 @@ typedef struct pk_client_params pk_client_params; struct DigestREQ; struct Kx509Request; +typedef struct kdc_request_desc *kdc_request_t; + #include +#define FAST_EXPIRATION_TIME (3 * 60) + +struct kdc_request_desc { + krb5_context context; + krb5_kdc_configuration *config; + + /* */ + + krb5_data request; + KDC_REQ req; + METHOD_DATA *padata; + + /* out */ + + METHOD_DATA outpadata; + + KDC_REP rep; + EncTicketPart et; + EncKDCRepPart ek; + + /* PA methods can affect both the reply key and the session key (pkinit) */ + krb5_enctype sessionetype; + krb5_keyblock reply_key; + krb5_keyblock session_key; + + const char *e_text; + + /* state */ + krb5_principal client_princ; + char *client_name; + hdb_entry_ex *client; + HDB *clientdb; + + krb5_principal server_princ; + char *server_name; + hdb_entry_ex *server; + + krb5_crypto armor_crypto; + + KDCFastState fast; +}; + + extern sig_atomic_t exit_flag; extern size_t max_request_udp; extern size_t max_request_tcp; diff --git a/kdc/kerberos5.c b/kdc/kerberos5.c index c13abb7ce..fb4a30419 100644 --- a/kdc/kerberos5.c +++ b/kdc/kerberos5.c @@ -241,6 +241,24 @@ _kdc_make_anonymous_principalname (PrincipalName *pn) return 0; } +static void +_kdc_r_log(kdc_request_t r, int level, const char *fmt, ...) +{ + va_list ap; + char *s; + va_start(ap, fmt); + s = kdc_log_msg_va(r->context, r->config, level, fmt, ap); + if(s) free(s); + va_end(ap); +} + +static void +_kdc_set_e_text(kdc_request_t r, const char *e_text) +{ + r->e_text = e_text; + kdc_log(r->context, r->config, 0, "%s", e_text); +} + void _kdc_log_timestamp(krb5_context context, krb5_kdc_configuration *config, @@ -271,6 +289,432 @@ _kdc_log_timestamp(krb5_context context, type, authtime_str, starttime_str, endtime_str, renewtime_str); } +/* + * + */ + +#ifdef PKINIT + +static krb5_error_code +pa_pkinit_validate(kdc_request_t r, const PA_DATA *pa) +{ + pk_client_params *pkp = NULL; + char *client_cert = NULL; + krb5_error_code ret; + + ret = _kdc_pk_rd_padata(r->context, r->config, &r->req, pa, r->client, &pkp); + if (ret || pkp == NULL) { + ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; + _kdc_r_log(r, 5, "Failed to decode PKINIT PA-DATA -- %s", + r->client_name); + goto out; + } + + ret = _kdc_pk_check_client(r->context, + r->config, + r->clientdb, + r->client, + pkp, + &client_cert); + if (ret) { + _kdc_set_e_text(r, "PKINIT certificate not allowed to " + "impersonate principal"); + goto out; + } + + _kdc_r_log(r, 0, "PKINIT pre-authentication succeeded -- %s using %s", + r->client_name, client_cert); + free(client_cert); + + ret = _kdc_pk_mk_pa_reply(r->context, r->config, pkp, r->client, + r->sessionetype, &r->req, &r->request, + &r->reply_key, &r->session_key, &r->outpadata); + if (ret) { + _kdc_set_e_text(r, "Failed to build PK-INIT reply"); + goto out; + } +#if 0 + ret = _kdc_add_inital_verified_cas(r->context, r->config, + pkp, &r->et); +#endif + out: + if (pkp) + _kdc_pk_free_client_param(r->context, pkp); + + return ret; +} + +#endif /* PKINIT */ + +/* + * + */ + +static krb5_error_code +make_pa_enc_challange(krb5_context context, METHOD_DATA *md, + krb5_crypto crypto) +{ + PA_ENC_TS_ENC p; + unsigned char *buf; + size_t buf_size; + size_t len; + EncryptedData encdata; + krb5_error_code ret; + int32_t usec; + int usec2; + + krb5_us_timeofday (context, &p.patimestamp, &usec); + usec2 = usec; + p.pausec = &usec2; + + ASN1_MALLOC_ENCODE(PA_ENC_TS_ENC, buf, buf_size, &p, &len, ret); + if (ret) + return ret; + if(buf_size != len) + krb5_abortx(context, "internal error in ASN.1 encoder"); + + ret = krb5_encrypt_EncryptedData(context, + crypto, + KRB5_KU_ENC_CHALLENGE_KDC, + buf, + len, + 0, + &encdata); + free(buf); + if (ret) + return ret; + + ASN1_MALLOC_ENCODE(EncryptedData, buf, buf_size, &encdata, &len, ret); + free_EncryptedData(&encdata); + if (ret) + return ret; + if(buf_size != len) + krb5_abortx(context, "internal error in ASN.1 encoder"); + + ret = krb5_padata_add(context, md, KRB5_PADATA_ENCRYPTED_CHALLENGE, buf, len); + if (ret) + free(buf); + return ret; +} + +static krb5_error_code +pa_enc_chal_validate(kdc_request_t r, const PA_DATA *pa) +{ + krb5_data pepper1, pepper2, ts_data; + KDC_REQ_BODY *b = &r->req.req_body; + EncryptedData enc_data; + krb5_enctype aenctype; + krb5_error_code ret; + struct Key *k; + size_t size; + int i; + + heim_assert(r->armor_crypto != NULL, "ENC-CHAL called for non FAST"); + + if (b->kdc_options.request_anonymous) { + ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; + kdc_log(r->context, r->config, 0, "ENC-CHALL doesn't support anon"); + return ret; + } + + ret = decode_EncryptedData(pa->padata_value.data, + pa->padata_value.length, + &enc_data, + &size); + if (ret) { + ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; + _kdc_r_log(r, 5, "Failed to decode PA-DATA -- %s", + r->client_name); + return ret; + } + + pepper1.data = "clientchallengearmor"; + pepper1.length = strlen(pepper1.data); + pepper2.data = "challengelongterm"; + pepper2.length = strlen(pepper2.data); + + krb5_crypto_getenctype(r->context, r->armor_crypto, &aenctype); + + for (i = 0; i < r->client->entry.keys.len; i++) { + krb5_crypto challangecrypto, longtermcrypto; + krb5_keyblock challangekey; + PA_ENC_TS_ENC p; + + k = &r->client->entry.keys.val[i]; + + ret = krb5_crypto_init(r->context, &k->key, 0, &longtermcrypto); + if (ret) + continue; + + ret = krb5_crypto_fx_cf2(r->context, r->armor_crypto, longtermcrypto, + &pepper1, &pepper2, aenctype, + &challangekey); + krb5_crypto_destroy(r->context, longtermcrypto); + if (ret) + continue; + + ret = krb5_crypto_init(r->context, &challangekey, 0, + &challangecrypto); + if (ret) + continue; + + ret = krb5_decrypt_EncryptedData(r->context, challangecrypto, + KRB5_KU_ENC_CHALLENGE_CLIENT, + &enc_data, + &ts_data); + if (ret) + continue; + + ret = decode_PA_ENC_TS_ENC(ts_data.data, + ts_data.length, + &p, + &size); + krb5_data_free(&ts_data); + if(ret){ + krb5_crypto_destroy(r->context, challangecrypto); + ret = KRB5KDC_ERR_PREAUTH_FAILED; + _kdc_r_log(r, 5, "Failed to decode PA-ENC-TS_ENC -- %s", + r->client_name); + continue; + } + + if (abs(kdc_time - p.patimestamp) > r->context->max_skew) { + char client_time[100]; + + krb5_crypto_destroy(r->context, challangecrypto); + + krb5_format_time(r->context, p.patimestamp, + client_time, sizeof(client_time), TRUE); + + ret = KRB5KRB_AP_ERR_SKEW; + _kdc_r_log(r, 0, "Too large time skew, " + "client time %s is out by %u > %u seconds -- %s", + client_time, + (unsigned)abs(kdc_time - p.patimestamp), + r->context->max_skew, + r->client_name); + + free_PA_ENC_TS_ENC(&p); + goto out; + } + + free_PA_ENC_TS_ENC(&p); + + ret = make_pa_enc_challange(r->context, &r->outpadata, + challangecrypto); + krb5_crypto_destroy(r->context, challangecrypto); + if (ret) + goto out; + + set_salt_padata(&r->outpadata, k->salt); + krb5_free_keyblock_contents(r->context, &r->reply_key); + ret = krb5_copy_keyblock_contents(r->context, &k->key, &r->reply_key); + ret = 0; + break; + } + if (i < r->client->entry.keys.len) + ret = KRB5KDC_ERR_PREAUTH_FAILED; + out: + free_EncryptedData(&enc_data); + + return ret; +} + +static krb5_error_code +pa_enc_ts_validate(kdc_request_t r, const PA_DATA *pa) +{ + EncryptedData enc_data; + krb5_error_code ret; + krb5_crypto crypto; + krb5_data ts_data; + PA_ENC_TS_ENC p; + size_t len; + Key *pa_key; + char *str; + + if (r->req.req_body.kdc_options.request_anonymous) { + ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; + _kdc_set_e_text(r, "ENC-TS doesn't suport anon"); + goto out; + } + + ret = decode_EncryptedData(pa->padata_value.data, + pa->padata_value.length, + &enc_data, + &len); + if (ret) { + ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; + _kdc_r_log(r, 5, "Failed to decode PA-DATA -- %s", + r->client_name); + goto out; + } + + ret = hdb_enctype2key(r->context, &r->client->entry, + enc_data.etype, &pa_key); + if(ret){ + char *estr; + _kdc_set_e_text(r, "No key matching entype"); + ret = KRB5KDC_ERR_ETYPE_NOSUPP; + if(krb5_enctype_to_string(r->context, enc_data.etype, &estr)) + estr = NULL; + if(estr == NULL) + _kdc_r_log(r, 5, + "No client key matching pa-data (%d) -- %s", + enc_data.etype, r->client_name); + else + _kdc_r_log(r, 5, + "No client key matching pa-data (%s) -- %s", + estr, r->client_name); + free(estr); + free_EncryptedData(&enc_data); + goto out; + } + + try_next_key: + ret = krb5_crypto_init(r->context, &pa_key->key, 0, &crypto); + if (ret) { + const char *msg = krb5_get_error_message(r->context, ret); + _kdc_r_log(r, 0, "krb5_crypto_init failed: %s", msg); + krb5_free_error_message(r->context, msg); + free_EncryptedData(&enc_data); + goto out; + } + + ret = krb5_decrypt_EncryptedData (r->context, + crypto, + KRB5_KU_PA_ENC_TIMESTAMP, + &enc_data, + &ts_data); + krb5_crypto_destroy(r->context, crypto); + /* + * Since the user might have several keys with the same + * enctype but with diffrent salting, we need to try all + * the keys with the same enctype. + */ + if(ret){ + krb5_error_code ret2; + const char *msg = krb5_get_error_message(r->context, ret); + + ret2 = krb5_enctype_to_string(r->context, + pa_key->key.keytype, &str); + if (ret2) + str = NULL; + _kdc_r_log(r, 5, "Failed to decrypt PA-DATA -- %s " + "(enctype %s) error %s", + r->client_name, str ? str : "unknown enctype", msg); + krb5_free_error_message(r->context, msg); + free(str); + + if(hdb_next_enctype2key(r->context, &r->client->entry, + enc_data.etype, &pa_key) == 0) + goto try_next_key; + + free_EncryptedData(&enc_data); + + if (r->clientdb->hdb_auth_status) + r->clientdb->hdb_auth_status(r->context, r->clientdb, r->client, + HDB_AUTH_WRONG_PASSWORD); + + ret = KRB5KDC_ERR_PREAUTH_FAILED; + goto out; + } + free_EncryptedData(&enc_data); + ret = decode_PA_ENC_TS_ENC(ts_data.data, + ts_data.length, + &p, + &len); + krb5_data_free(&ts_data); + if(ret){ + ret = KRB5KDC_ERR_PREAUTH_FAILED; + _kdc_r_log(r, 5, "Failed to decode PA-ENC-TS_ENC -- %s", + r->client_name); + goto out; + } + if (abs(kdc_time - p.patimestamp) > r->context->max_skew) { + char client_time[100]; + + krb5_format_time(r->context, p.patimestamp, + client_time, sizeof(client_time), TRUE); + + ret = KRB5KRB_AP_ERR_SKEW; + _kdc_r_log(r, 0, "Too large time skew, " + "client time %s is out by %u > %u seconds -- %s", + client_time, + (unsigned)abs(kdc_time - p.patimestamp), + r->context->max_skew, + r->client_name); + + /* + * The following is needed to make windows clients to + * retry using the timestamp in the error message, if + * there is a e_text, they become unhappy. + */ + r->e_text = NULL; + free_PA_ENC_TS_ENC(&p); + goto out; + } + free_PA_ENC_TS_ENC(&p); + + set_salt_padata(&r->outpadata, pa_key->salt); + + ret = krb5_copy_keyblock_contents(r->context, &pa_key->key, &r->reply_key); + if (ret) + return ret; + + ret = krb5_enctype_to_string(r->context, pa_key->key.keytype, &str); + if (ret) + str = NULL; + _kdc_r_log(r, 2, "ENC-TS Pre-authentication succeeded -- %s using %s", + r->client_name, str ? str : "unknown enctype"); + free(str); + + ret = 0; + + out: + + return ret; +} + +struct kdc_patypes { + int type; + char *name; + unsigned int flags; +#define PA_ANNOUNCE 1 +#define PA_REQ_FAST 2 /* only use inside fast */ + krb5_error_code (*validate)(kdc_request_t, const PA_DATA *pa); +}; + +static const struct kdc_patypes pat[] = { +#ifdef PKINIT + { + KRB5_PADATA_PK_AS_REQ, "PK-INIT(ietf)", PA_ANNOUNCE, + pa_pkinit_validate + }, + { + KRB5_PADATA_PK_AS_REQ_WIN, "PK-INIT(win2k)", PA_ANNOUNCE, + pa_pkinit_validate + }, +#else + { KRB5_PADATA_PK_AS_REQ, "PK-INIT(ietf)", 0, NULL }, + { KRB5_PADATA_PK_AS_REQ_WIN, "PK-INIT(win2k)", 0, NULL }, +#endif + { KRB5_PADATA_PA_PK_OCSP_RESPONSE , "OCSP", 0, NULL }, + { + KRB5_PADATA_ENC_TIMESTAMP , "ENC-TS", + PA_ANNOUNCE, + pa_enc_ts_validate + }, + { + KRB5_PADATA_ENCRYPTED_CHALLENGE , "ENC-CHAL", + PA_ANNOUNCE | PA_REQ_FAST, + pa_enc_chal_validate + }, + { KRB5_PADATA_REQ_ENC_PA_REP , "REQ-ENC-PA-REP", 0, NULL }, + { KRB5_PADATA_FX_FAST, "FX-FAST", PA_ANNOUNCE, NULL }, + { KRB5_PADATA_FX_ERROR, "FX-ERROR", 0, NULL }, + { KRB5_PADATA_FX_COOKIE, "FX-COOKIE", 0, NULL } +}; + static void log_patypes(krb5_context context, krb5_kdc_configuration *config, @@ -278,27 +722,18 @@ log_patypes(krb5_context context, { struct rk_strpool *p = NULL; char *str; - size_t i; - - for (i = 0; i < padata->len; i++) { - switch(padata->val[i].padata_type) { - case KRB5_PADATA_PK_AS_REQ: - p = rk_strpoolprintf(p, "PK-INIT(ietf)"); - break; - case KRB5_PADATA_PK_AS_REQ_WIN: - p = rk_strpoolprintf(p, "PK-INIT(win2k)"); - break; - case KRB5_PADATA_PA_PK_OCSP_RESPONSE: - p = rk_strpoolprintf(p, "OCSP"); - break; - case KRB5_PADATA_ENC_TIMESTAMP: - p = rk_strpoolprintf(p, "encrypted-timestamp"); - break; - default: - p = rk_strpoolprintf(p, "%d", padata->val[i].padata_type); - break; + size_t n, m; + + for (n = 0; n < padata->len; n++) { + for (m = 0; m < sizeof(pat) / sizeof(pat[0]); m++) { + if (padata->val[n].padata_type == pat[m].type) { + p = rk_strpoolprintf(p, "%s", pat[m].name); + break; + } } - if (p && i + 1 < padata->len) + if (m == sizeof(pat) / sizeof(pat[0])) + p = rk_strpoolprintf(p, "%d", padata->val[n].padata_type); + if (p && n + 1 < padata->len) p = rk_strpoolprintf(p, ", "); if (p == NULL) { kdc_log(context, config, 0, "out of memory"); @@ -317,11 +752,11 @@ log_patypes(krb5_context context, * */ - krb5_error_code _kdc_encode_reply(krb5_context context, krb5_kdc_configuration *config, - KDC_REP *rep, const EncTicketPart *et, EncKDCRepPart *ek, + krb5_crypto armor_crypto, uint32_t nonce, + KDC_REP *rep, EncTicketPart *et, EncKDCRepPart *ek, krb5_enctype etype, int skvno, const EncryptionKey *skey, int ckvno, const EncryptionKey *reply_key, @@ -342,18 +777,12 @@ _kdc_encode_reply(krb5_context context, krb5_free_error_message(context, msg); return ret; } - if(buf_size != len) { - free(buf); - kdc_log(context, config, 0, "Internal error in ASN.1 encoder"); - *e_text = "KDC internal error"; - return KRB5KRB_ERR_GENERIC; - } + if(buf_size != len) + krb5_abortx(context, "Internal error in ASN.1 encoder"); ret = krb5_crypto_init(context, skey, etype, &crypto); if (ret) { - const char *msg; - free(buf); - msg = krb5_get_error_message(context, ret); + const char *msg = krb5_get_error_message(context, ret); kdc_log(context, config, 0, "krb5_crypto_init failed: %s", msg); krb5_free_error_message(context, msg); return ret; @@ -375,6 +804,69 @@ _kdc_encode_reply(krb5_context context, return ret; } + if (armor_crypto) { + krb5_data data; + krb5_keyblock *strengthen_key = NULL; + KrbFastFinished finished; + + kdc_log(context, config, 0, "FAST armor protection"); + + memset(&finished, 0, sizeof(finished)); + krb5_data_zero(&data); + + finished.timestamp = kdc_time; + finished.usec = 0; + finished.crealm = et->crealm; + finished.cname = et->cname; + + ASN1_MALLOC_ENCODE(Ticket, data.data, data.length, + &rep->ticket, &len, ret); + if (ret) + return ret; + if (data.length != len) + krb5_abortx(context, "internal asn.1 error"); + + ret = krb5_create_checksum(context, armor_crypto, + KRB5_KU_FAST_FINISHED, 0, + data.data, data.length, + &finished.ticket_checksum); + krb5_data_free(&data); + if (ret) + return ret; + + ret = _kdc_fast_mk_response(context, armor_crypto, + rep->padata, strengthen_key, &finished, + nonce, &data); + free_Checksum(&finished.ticket_checksum); + if (ret) + return ret; + + if (rep->padata) { + free_METHOD_DATA(rep->padata); + } else { + rep->padata = calloc(1, sizeof(*(rep->padata))); + if (rep->padata == NULL) { + krb5_data_free(&data); + return ENOMEM; + } + } + + ret = krb5_padata_add(context, rep->padata, + KRB5_PADATA_FX_FAST, + data.data, data.length); + if (ret) + return ret; + + /* + * Hide client name of privacy reasons + */ + if (1 /* r->fast_options.hide_client_names */) { + rep->crealm[0] = '\0'; + free_PrincipalName(&rep->cname); + rep->cname.name_type = 0; + } + } + if(rep->msg_type == krb_as_rep && !config->encode_as_rep_as_tgs_rep) ASN1_MALLOC_ENCODE(EncASRepPart, buf, buf_size, ek, &len, ret); else @@ -937,6 +1429,50 @@ send_pac_p(krb5_context context, KDC_REQ *req) return TRUE; } +/* + * + */ + +static krb5_error_code +generate_pac(kdc_request_t r, Key *skey) +{ + krb5_error_code ret; + krb5_pac p = NULL; + krb5_data data; + + ret = _kdc_pac_generate(r->context, r->client, &p); + if (ret) { + _kdc_r_log(r, 0, "PAC generation failed for -- %s", + r->client_name); + return ret; + } + if (p == NULL) + return 0; + + ret = _krb5_pac_sign(r->context, p, r->et.authtime, + r->client->entry.principal, + &skey->key, /* Server key */ + &skey->key, /* FIXME: should be krbtgt key */ + &data); + krb5_pac_free(r->context, p); + if (ret) { + _kdc_r_log(r, 0, "PAC signing failed for -- %s", + r->client_name); + return ret; + } + + ret = _kdc_tkt_add_if_relevant_ad(r->context, &r->et, + KRB5_AUTHDATA_WIN2K_PAC, + &data); + krb5_data_free(&data); + + return ret; +} + +/* + * + */ + krb5_boolean _kdc_is_anonymous(krb5_context context, krb5_principal principal) { @@ -948,62 +1484,117 @@ _kdc_is_anonymous(krb5_context context, krb5_principal principal) return 1; } +static int +require_preauth_p(kdc_request_t r) +{ + return r->config->require_preauth + || r->client->entry.flags.require_preauth + || r->server->entry.flags.require_preauth; +} + + +/* + * + */ + +static krb5_error_code +add_enc_pa_rep(kdc_request_t r) +{ + krb5_error_code ret; + krb5_crypto crypto; + Checksum checksum; + krb5_data cdata; + size_t len; + + r->et.flags.enc_pa_rep = r->ek.flags.enc_pa_rep = 1; + + ret = krb5_crypto_init(r->context, &r->reply_key, 0, &crypto); + if (ret) + return ret; + + ret = krb5_create_checksum(r->context, crypto, + KRB5_KU_AS_REQ, 0, + r->request.data, r->request.length, + &checksum); + krb5_crypto_destroy(r->context, crypto); + if (ret) + return ret; + + ASN1_MALLOC_ENCODE(Checksum, cdata.data, cdata.length, + &checksum, &len, ret); + free_Checksum(&checksum); + if (ret) + return ret; + heim_assert(cdata.length == len, "ASN.1 internal error"); + + if (r->ek.encrypted_pa_data == NULL) { + ALLOC(r->ek.encrypted_pa_data); + if (r->ek.encrypted_pa_data == NULL) + return ENOMEM; + } + ret = krb5_padata_add(r->context, r->ek.encrypted_pa_data, + KRB5_PADATA_REQ_ENC_PA_REP, cdata.data, cdata.length); + if (ret) + return ret; + + return krb5_padata_add(r->context, r->ek.encrypted_pa_data, + KRB5_PADATA_FX_FAST, NULL, 0); +} + /* * */ krb5_error_code -_kdc_as_rep(krb5_context context, - krb5_kdc_configuration *config, - KDC_REQ *req, - const krb5_data *req_buffer, +_kdc_as_rep(kdc_request_t r, krb5_data *reply, const char *from, struct sockaddr *from_addr, int datagram_reply) { - KDC_REQ_BODY *b = &req->req_body; + krb5_context context = r->context; + krb5_kdc_configuration *config = r->config; + KDC_REQ *req = &r->req; + KDC_REQ_BODY *b = NULL; AS_REP rep; - KDCOptions f = b->kdc_options; - hdb_entry_ex *client = NULL, *server = NULL; - HDB *clientdb; - krb5_enctype setype, sessionetype; - krb5_data e_data; - EncTicketPart et; - EncKDCRepPart ek; - krb5_principal client_princ = NULL, server_princ = NULL; - char *client_name = NULL, *server_name = NULL; + KDCOptions f; + krb5_enctype setype; krb5_error_code ret = 0; - const char *e_text = NULL; - krb5_crypto crypto; Key *ckey, *skey; - EncryptionKey *reply_key = NULL, session_key; - int flags = HDB_F_FOR_AS_REQ; -#ifdef PKINIT - pk_client_params *pkp = NULL; -#endif + int found_pa = 0; + int i, flags = HDB_F_FOR_AS_REQ; + METHOD_DATA error_method; + const PA_DATA *pa; memset(&rep, 0, sizeof(rep)); - memset(&session_key, 0, sizeof(session_key)); - krb5_data_zero(&e_data); + error_method.len = 0; + error_method.val = NULL; - ALLOC(rep.padata); - rep.padata->len = 0; - rep.padata->val = NULL; + /* + * Look for FAST armor and unwrap + */ + ret = _kdc_fast_unwrap_request(r); + if (ret) { + _kdc_r_log(r, 0, "FAST unwrap request from %s failed: %d", from, ret); + goto out; + } + + b = &req->req_body; + f = b->kdc_options; if (f.canonicalize) flags |= HDB_F_CANON; if(b->sname == NULL){ ret = KRB5KRB_ERR_GENERIC; - e_text = "No server in request"; + _kdc_set_e_text(r, "No server in request"); } else{ ret = _krb5_principalname2krb5_principal (context, - &server_princ, + &r->server_princ, *(b->sname), b->realm); if (ret == 0) - ret = krb5_unparse_name(context, server_princ, &server_name); + ret = krb5_unparse_name(context, r->server_princ, &r->server_name); } if (ret) { kdc_log(context, config, 0, @@ -1012,16 +1603,16 @@ _kdc_as_rep(krb5_context context, } if(b->cname == NULL){ ret = KRB5KRB_ERR_GENERIC; - e_text = "No client in request"; + _kdc_set_e_text(r, "No client in request"); } else { ret = _krb5_principalname2krb5_principal (context, - &client_princ, + &r->client_princ, *(b->cname), b->realm); if (ret) goto out; - ret = krb5_unparse_name(context, client_princ, &client_name); + ret = krb5_unparse_name(context, r->client_princ, &r->client_name); } if (ret) { kdc_log(context, config, 0, @@ -1030,13 +1621,13 @@ _kdc_as_rep(krb5_context context, } kdc_log(context, config, 0, "AS-REQ %s from %s for %s", - client_name, from, server_name); + r->client_name, from, r->server_name); /* * */ - if (_kdc_is_anonymous(context, client_princ)) { + if (_kdc_is_anonymous(context, r->client_princ)) { if (!b->kdc_options.request_anonymous) { kdc_log(context, config, 0, "Anonymous ticket w/o anonymous flag"); ret = KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN; @@ -1045,7 +1636,7 @@ _kdc_as_rep(krb5_context context, } else if (b->kdc_options.request_anonymous) { kdc_log(context, config, 0, "Request for a anonymous ticket with non " - "anonymous client name: %s", client_name); + "anonymous client name: %s", r->client_name); ret = KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN; goto out; } @@ -1054,36 +1645,35 @@ _kdc_as_rep(krb5_context context, * */ - ret = _kdc_db_fetch(context, config, client_princ, + ret = _kdc_db_fetch(context, config, r->client_princ, HDB_F_GET_CLIENT | flags, NULL, - &clientdb, &client); + &r->clientdb, &r->client); if(ret == HDB_ERR_NOT_FOUND_HERE) { - kdc_log(context, config, 5, "client %s does not have secrets at this KDC, need to proxy", client_name); + kdc_log(context, config, 5, "client %s does not have secrets at this KDC, need to proxy", + r->client_name); goto out; } else if(ret){ const char *msg = krb5_get_error_message(context, ret); - kdc_log(context, config, 0, "UNKNOWN -- %s: %s", client_name, msg); + kdc_log(context, config, 0, "UNKNOWN -- %s: %s", r->client_name, msg); krb5_free_error_message(context, msg); ret = KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN; goto out; } - ret = _kdc_db_fetch(context, config, server_princ, + ret = _kdc_db_fetch(context, config, r->server_princ, HDB_F_GET_SERVER|HDB_F_GET_KRBTGT | flags, - NULL, NULL, &server); + NULL, NULL, &r->server); if(ret == HDB_ERR_NOT_FOUND_HERE) { - kdc_log(context, config, 5, "target %s does not have secrets at this KDC, need to proxy", server_name); + kdc_log(context, config, 5, "target %s does not have secrets at this KDC, need to proxy", + r->server_name); goto out; } else if(ret){ const char *msg = krb5_get_error_message(context, ret); - kdc_log(context, config, 0, "UNKNOWN -- %s: %s", server_name, msg); + kdc_log(context, config, 0, "UNKNOWN -- %s: %s", r->server_name, msg); krb5_free_error_message(context, msg); ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN; goto out; } - memset(&et, 0, sizeof(et)); - memset(&ek, 0, sizeof(ek)); - /* * Select a session enctype from the list of the crypto system * supported enctypes that is supported by the client and is one of @@ -1094,307 +1684,71 @@ _kdc_as_rep(krb5_context context, * enctype that an older version of a KDC in the same realm can't * decrypt. */ + ret = _kdc_find_etype(context, config->as_use_strongest_session_key, FALSE, - client, b->etype.val, b->etype.len, &sessionetype, + r->client, b->etype.val, b->etype.len, &r->sessionetype, NULL); if (ret) { kdc_log(context, config, 0, "Client (%s) from %s has no common enctypes with KDC " "to use for the session key", - client_name, from); + r->client_name, from); goto out; } - /* - * But if the KDC admin is paranoid and doesn't want to have "not - * the best" enctypes on the krbtgt, lets save the best pick from - * the client list and hope that that will work for any other - * KDCs. - */ /* * Pre-auth processing */ if(req->padata){ + unsigned int n; int i; - const PA_DATA *pa; - int found_pa = 0; log_patypes(context, config, req->padata); -#ifdef PKINIT - kdc_log(context, config, 5, - "Looking for PKINIT pa-data -- %s", client_name); + /* Check if preauth matching */ - e_text = "No PKINIT PA found"; + for (n = 0; !found_pa && n < sizeof(pat) / sizeof(pat[0]); n++) { + if (pat[n].validate == NULL) + continue; + if (r->armor_crypto == NULL && (pat[n].flags & PA_REQ_FAST)) + continue; - i = 0; - pa = _kdc_find_padata(req, &i, KRB5_PADATA_PK_AS_REQ); - if (pa == NULL) { + kdc_log(context, config, 5, + "Looking for %s pa-data -- %s", pat[n].name, r->client_name); i = 0; - pa = _kdc_find_padata(req, &i, KRB5_PADATA_PK_AS_REQ_WIN); + pa = _kdc_find_padata(req, &i, pat[n].type); + if (pa) { + ret = pat[n].validate(r, pa); + if (ret == 0) { + kdc_log(context, config, 0, + "%s pre-authentication succeeded -- %s", + pat[n].name, r->client_name); + found_pa = 1; + r->et.flags.pre_authent = 1; + } + } } - if (pa) { - char *client_cert = NULL; + } - ret = _kdc_pk_rd_padata(context, config, req, pa, client, &pkp); - if (ret) { - ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; - kdc_log(context, config, 5, - "Failed to decode PKINIT PA-DATA -- %s", - client_name); - goto ts_enc; - } - if (ret == 0 && pkp == NULL) - goto ts_enc; - - ret = _kdc_pk_check_client(context, - config, - clientdb, - client, - pkp, - &client_cert); - if (ret) { - e_text = "PKINIT certificate not allowed to " - "impersonate principal"; - _kdc_pk_free_client_param(context, pkp); - - kdc_log(context, config, 0, "%s", e_text); - pkp = NULL; - goto out; - } - - found_pa = 1; - et.flags.pre_authent = 1; - kdc_log(context, config, 0, - "PKINIT pre-authentication succeeded -- %s using %s", - client_name, client_cert); - free(client_cert); - if (pkp) - goto preauth_done; - } - ts_enc: -#endif - kdc_log(context, config, 5, "Looking for ENC-TS pa-data -- %s", - client_name); - - i = 0; - e_text = "No ENC-TS found"; - while((pa = _kdc_find_padata(req, &i, KRB5_PADATA_ENC_TIMESTAMP))){ - krb5_data ts_data; - PA_ENC_TS_ENC p; - size_t len; - EncryptedData enc_data; - Key *pa_key; - char *str; - - found_pa = 1; - - if (b->kdc_options.request_anonymous) { - ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; - kdc_log(context, config, 0, "ENC-TS doesn't support anon"); - goto out; - } - - ret = decode_EncryptedData(pa->padata_value.data, - pa->padata_value.length, - &enc_data, - &len); - if (ret) { - ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; - kdc_log(context, config, 5, "Failed to decode PA-DATA -- %s", - client_name); - goto out; - } - - ret = hdb_enctype2key(context, &client->entry, - enc_data.etype, &pa_key); - if(ret){ - char *estr; - e_text = "No key matches pa-data"; - ret = KRB5KDC_ERR_ETYPE_NOSUPP; - if(krb5_enctype_to_string(context, enc_data.etype, &estr)) - estr = NULL; - if(estr == NULL) - kdc_log(context, config, 5, - "No client key matching pa-data (%d) -- %s", - enc_data.etype, client_name); - else - kdc_log(context, config, 5, - "No client key matching pa-data (%s) -- %s", - estr, client_name); - free(estr); - free_EncryptedData(&enc_data); + if (found_pa == 0 && (require_preauth_p(r) || b->kdc_options.request_anonymous)) { + size_t n; + for (n = 0; n < sizeof(pat) / sizeof(pat[0]); n++) { + if ((pat[n].flags & PA_ANNOUNCE) == 0) continue; - } - - try_next_key: - ret = krb5_crypto_init(context, &pa_key->key, 0, &crypto); - if (ret) { - const char *msg = krb5_get_error_message(context, ret); - kdc_log(context, config, 0, "krb5_crypto_init failed: %s", msg); - krb5_free_error_message(context, msg); - free_EncryptedData(&enc_data); - continue; - } - - ret = krb5_decrypt_EncryptedData (context, - crypto, - KRB5_KU_PA_ENC_TIMESTAMP, - &enc_data, - &ts_data); - krb5_crypto_destroy(context, crypto); - /* - * Since the user might have several keys with the same - * enctype but with diffrent salting, we need to try all - * the keys with the same enctype. - */ - if(ret){ - krb5_error_code ret2; - const char *msg = krb5_get_error_message(context, ret); - - ret2 = krb5_enctype_to_string(context, - pa_key->key.keytype, &str); - if (ret2) - str = NULL; - kdc_log(context, config, 5, - "Failed to decrypt PA-DATA -- %s " - "(enctype %s) error %s", - client_name, str ? str : "unknown enctype", msg); - krb5_free_error_message(context, msg); - free(str); - - if(hdb_next_enctype2key(context, &client->entry, - enc_data.etype, &pa_key) == 0) - goto try_next_key; - e_text = "Failed to decrypt PA-DATA"; - - free_EncryptedData(&enc_data); - - if (clientdb->hdb_auth_status) - (clientdb->hdb_auth_status)(context, clientdb, client, HDB_AUTH_WRONG_PASSWORD); - - ret = KRB5KDC_ERR_PREAUTH_FAILED; - continue; - } - free_EncryptedData(&enc_data); - ret = decode_PA_ENC_TS_ENC(ts_data.data, - ts_data.length, - &p, - &len); - krb5_data_free(&ts_data); - if(ret){ - e_text = "Failed to decode PA-ENC-TS-ENC"; - ret = KRB5KDC_ERR_PREAUTH_FAILED; - kdc_log(context, config, - 5, "Failed to decode PA-ENC-TS_ENC -- %s", - client_name); - continue; - } - free_PA_ENC_TS_ENC(&p); - if (abs(kdc_time - p.patimestamp) > context->max_skew) { - char client_time[100]; - - krb5_format_time(context, p.patimestamp, - client_time, sizeof(client_time), TRUE); - - ret = KRB5KRB_AP_ERR_SKEW; - kdc_log(context, config, 0, - "Too large time skew, " - "client time %s is out by %u > %u seconds -- %s", - client_time, - (unsigned)abs(kdc_time - p.patimestamp), - context->max_skew, - client_name); - - /* - * The following is needed to make windows clients to - * retry using the timestamp in the error message, if - * there is a e_text, they become unhappy. - */ - e_text = NULL; - goto out; - } - et.flags.pre_authent = 1; - - set_salt_padata(rep.padata, pa_key->salt); - - reply_key = &pa_key->key; - - ret = krb5_enctype_to_string(context, pa_key->key.keytype, &str); + ret = krb5_padata_add(context, &error_method, + pat[n].type, NULL, 0); if (ret) - str = NULL; - - kdc_log(context, config, 2, - "ENC-TS Pre-authentication succeeded -- %s using %s", - client_name, str ? str : "unknown enctype"); - free(str); - break; + goto out; } -#ifdef PKINIT - preauth_done: -#endif - if(found_pa == 0 && config->require_preauth) - goto use_pa; - /* We come here if we found a pa-enc-timestamp, but if there - was some problem with it, other than too large skew */ - if(found_pa && et.flags.pre_authent == 0){ - kdc_log(context, config, 0, "%s -- %s", e_text, client_name); - e_text = NULL; - goto out; - } - }else if (config->require_preauth - || b->kdc_options.request_anonymous /* hack to force anon */ - || client->entry.flags.require_preauth - || server->entry.flags.require_preauth) { - METHOD_DATA method_data; - PA_DATA *pa; - unsigned char *buf; - size_t len; - - use_pa: - method_data.len = 0; - method_data.val = NULL; - - ret = realloc_method_data(&method_data); - if (ret) { - free_METHOD_DATA(&method_data); - goto out; - } - pa = &method_data.val[method_data.len-1]; - pa->padata_type = KRB5_PADATA_ENC_TIMESTAMP; - pa->padata_value.length = 0; - pa->padata_value.data = NULL; - -#ifdef PKINIT - ret = realloc_method_data(&method_data); - if (ret) { - free_METHOD_DATA(&method_data); - goto out; - } - pa = &method_data.val[method_data.len-1]; - pa->padata_type = KRB5_PADATA_PK_AS_REQ; - pa->padata_value.length = 0; - pa->padata_value.data = NULL; - - ret = realloc_method_data(&method_data); - if (ret) { - free_METHOD_DATA(&method_data); - goto out; - } - pa = &method_data.val[method_data.len-1]; - pa->padata_type = KRB5_PADATA_PK_AS_REQ_WIN; - pa->padata_value.length = 0; - pa->padata_value.data = NULL; -#endif /* * If there is a client key, send ETYPE_INFO{,2} */ ret = _kdc_find_etype(context, config->preauth_use_strongest_session_key, TRUE, - client, b->etype.val, b->etype.len, NULL, &ckey); + r->client, b->etype.val, b->etype.len, NULL, &ckey); if (ret == 0) { /* @@ -1411,57 +1765,43 @@ _kdc_as_rep(krb5_context context, if (older_enctype(ckey->key.keytype)) { ret = get_pa_etype_info(context, config, - &method_data, ckey); - if (ret) { - free_METHOD_DATA(&method_data); + &error_method, ckey); + if (ret) goto out; - } } ret = get_pa_etype_info2(context, config, - &method_data, ckey); - if (ret) { - free_METHOD_DATA(&method_data); + &error_method, ckey); + if (ret) goto out; - } } - ASN1_MALLOC_ENCODE(METHOD_DATA, buf, len, &method_data, &len, ret); - free_METHOD_DATA(&method_data); - - e_data.data = buf; - e_data.length = len; - e_text ="Need to use PA-ENC-TIMESTAMP/PA-PK-AS-REQ", - ret = KRB5KDC_ERR_PREAUTH_REQUIRED; - - kdc_log(context, config, 0, - "No preauth found, returning PREAUTH-REQUIRED -- %s", - client_name); + _kdc_set_e_text(r, "Need to use PA-ENC-TIMESTAMP/PA-PK-AS-REQ"); goto out; } - if (clientdb->hdb_auth_status) - (clientdb->hdb_auth_status)(context, clientdb, client, - HDB_AUTH_SUCCESS); + if (r->clientdb->hdb_auth_status) + r->clientdb->hdb_auth_status(context, r->clientdb, r->client, + HDB_AUTH_SUCCESS); /* * Verify flags after the user been required to prove its identity * with in a preauth mech. */ - ret = _kdc_check_access(context, config, client, client_name, - server, server_name, - req, &e_data); + ret = _kdc_check_access(context, config, r->client, r->client_name, + r->server, r->server_name, + req, &error_method); if(ret) goto out; /* - * Selelct the best encryption type for the KDC with out regard to + * Select the best encryption type for the KDC with out regard to * the client since the client never needs to read that data. */ ret = _kdc_get_preferred_key(context, config, - server, server_name, + r->server, r->server_name, &setype, &skey); if(ret) goto out; @@ -1469,25 +1809,28 @@ _kdc_as_rep(krb5_context context, if(f.renew || f.validate || f.proxy || f.forwarded || f.enc_tkt_in_skey || (f.request_anonymous && !config->allow_anonymous)) { ret = KRB5KDC_ERR_BADOPTION; - e_text = "Bad KDC options"; - kdc_log(context, config, 0, "Bad KDC options -- %s", client_name); + _kdc_set_e_text(r, "Bad KDC options"); goto out; } + /* + * Build reply + */ + rep.pvno = 5; rep.msg_type = krb_as_rep; - ret = copy_Realm(&client->entry.principal->realm, &rep.crealm); + ret = copy_Realm(&r->client->entry.principal->realm, &rep.crealm); if (ret) goto out; - ret = _krb5_principal2principalname(&rep.cname, client->entry.principal); + ret = _krb5_principal2principalname(&rep.cname, r->client->entry.principal); if (ret) goto out; rep.ticket.tkt_vno = 5; - copy_Realm(&server->entry.principal->realm, &rep.ticket.realm); + copy_Realm(&r->server->entry.principal->realm, &rep.ticket.realm); _krb5_principal2principalname(&rep.ticket.sname, - server->entry.principal); + r->server->entry.principal); /* java 1.6 expects the name to be the same type, lets allow that * uncomplicated name-types. */ #define CNT(sp,t) (((sp)->sname->name_type) == KRB5_NT_##t) @@ -1495,77 +1838,69 @@ _kdc_as_rep(krb5_context context, rep.ticket.sname.name_type = b->sname->name_type; #undef CNT - et.flags.initial = 1; - if(client->entry.flags.forwardable && server->entry.flags.forwardable) - et.flags.forwardable = f.forwardable; + r->et.flags.initial = 1; + if(r->client->entry.flags.forwardable && r->server->entry.flags.forwardable) + r->et.flags.forwardable = f.forwardable; else if (f.forwardable) { - e_text = "Ticket may not be forwardable"; + _kdc_set_e_text(r, "Ticket may not be forwardable"); ret = KRB5KDC_ERR_POLICY; - kdc_log(context, config, 0, - "Ticket may not be forwardable -- %s", client_name); goto out; } - if(client->entry.flags.proxiable && server->entry.flags.proxiable) - et.flags.proxiable = f.proxiable; + if(r->client->entry.flags.proxiable && r->server->entry.flags.proxiable) + r->et.flags.proxiable = f.proxiable; else if (f.proxiable) { - e_text = "Ticket may not be proxiable"; + _kdc_set_e_text(r, "Ticket may not be proxiable"); ret = KRB5KDC_ERR_POLICY; - kdc_log(context, config, 0, - "Ticket may not be proxiable -- %s", client_name); goto out; } - if(client->entry.flags.postdate && server->entry.flags.postdate) - et.flags.may_postdate = f.allow_postdate; + if(r->client->entry.flags.postdate && r->server->entry.flags.postdate) + r->et.flags.may_postdate = f.allow_postdate; else if (f.allow_postdate){ - e_text = "Ticket may not be postdate"; + _kdc_set_e_text(r, "Ticket may not be postdate"); ret = KRB5KDC_ERR_POLICY; - kdc_log(context, config, 0, - "Ticket may not be postdatable -- %s", client_name); goto out; } /* check for valid set of addresses */ if(!_kdc_check_addresses(context, config, b->addresses, from_addr)) { - e_text = "Bad address list in requested"; + _kdc_set_e_text(r, "Bad address list in requested"); ret = KRB5KRB_AP_ERR_BADADDR; - kdc_log(context, config, 0, - "Bad address list requested -- %s", client_name); goto out; } - ret = copy_PrincipalName(&rep.cname, &et.cname); + ret = copy_PrincipalName(&rep.cname, &r->et.cname); if (ret) goto out; - ret = copy_Realm(&rep.crealm, &et.crealm); + ret = copy_Realm(&rep.crealm, &r->et.crealm); if (ret) goto out; { time_t start; time_t t; - - start = et.authtime = kdc_time; + + start = r->et.authtime = kdc_time; if(f.postdated && req->req_body.from){ - ALLOC(et.starttime); - start = *et.starttime = *req->req_body.from; - et.flags.invalid = 1; - et.flags.postdated = 1; /* XXX ??? */ + ALLOC(r->et.starttime); + start = *r->et.starttime = *req->req_body.from; + r->et.flags.invalid = 1; + r->et.flags.postdated = 1; /* XXX ??? */ } _kdc_fix_time(&b->till); t = *b->till; /* be careful not overflowing */ - if(client->entry.max_life) - t = start + min(t - start, *client->entry.max_life); - if(server->entry.max_life) - t = start + min(t - start, *server->entry.max_life); + if(r->client->entry.max_life) + t = start + min(t - start, *r->client->entry.max_life); + if(r->server->entry.max_life) + t = start + min(t - start, *r->server->entry.max_life); #if 0 t = min(t, start + realm->max_life); #endif - et.endtime = t; - if(f.renewable_ok && et.endtime < *b->till){ + r->et.endtime = t; + if(f.renewable_ok && r->et.endtime < *b->till){ f.renewable = 1; if(b->rtime == NULL){ ALLOC(b->rtime); @@ -1578,29 +1913,29 @@ _kdc_as_rep(krb5_context context, t = *b->rtime; if(t == 0) t = MAX_TIME; - if(client->entry.max_renew) - t = start + min(t - start, *client->entry.max_renew); - if(server->entry.max_renew) - t = start + min(t - start, *server->entry.max_renew); + if(r->client->entry.max_renew) + t = start + min(t - start, *r->client->entry.max_renew); + if(r->server->entry.max_renew) + t = start + min(t - start, *r->server->entry.max_renew); #if 0 t = min(t, start + realm->max_renew); #endif - ALLOC(et.renew_till); - *et.renew_till = t; - et.flags.renewable = 1; + ALLOC(r->et.renew_till); + *r->et.renew_till = t; + r->et.flags.renewable = 1; } } if (f.request_anonymous) - et.flags.anonymous = 1; + r->et.flags.anonymous = 1; if(b->addresses){ - ALLOC(et.caddr); - copy_HostAddresses(b->addresses, et.caddr); + ALLOC(r->et.caddr); + copy_HostAddresses(b->addresses, r->et.caddr); } - et.transited.tr_type = DOMAIN_X500_COMPRESS; - krb5_data_zero(&et.transited.contents); + r->et.transited.tr_type = DOMAIN_X500_COMPRESS; + krb5_data_zero(&r->et.transited.contents); /* The MIT ASN.1 library (obviously) doesn't tell lengths encoded * as 0 and as 0x80 (meaning indefinite length) apart, and is thus @@ -1611,94 +1946,88 @@ _kdc_as_rep(krb5_context context, * If there's a pw_end or valid_end we will use that, * otherwise just a dummy lr. */ - ek.last_req.val = malloc(2 * sizeof(*ek.last_req.val)); - if (ek.last_req.val == NULL) { + r->ek.last_req.val = malloc(2 * sizeof(*r->ek.last_req.val)); + if (r->ek.last_req.val == NULL) { ret = ENOMEM; goto out; } - ek.last_req.len = 0; - if (client->entry.pw_end + r->ek.last_req.len = 0; + if (r->client->entry.pw_end && (config->kdc_warn_pwexpire == 0 - || kdc_time + config->kdc_warn_pwexpire >= *client->entry.pw_end)) { - ek.last_req.val[ek.last_req.len].lr_type = LR_PW_EXPTIME; - ek.last_req.val[ek.last_req.len].lr_value = *client->entry.pw_end; - ++ek.last_req.len; + || kdc_time + config->kdc_warn_pwexpire >= *r->client->entry.pw_end)) { + r->ek.last_req.val[r->ek.last_req.len].lr_type = LR_PW_EXPTIME; + r->ek.last_req.val[r->ek.last_req.len].lr_value = *r->client->entry.pw_end; + ++r->ek.last_req.len; } - if (client->entry.valid_end) { - ek.last_req.val[ek.last_req.len].lr_type = LR_ACCT_EXPTIME; - ek.last_req.val[ek.last_req.len].lr_value = *client->entry.valid_end; - ++ek.last_req.len; + if (r->client->entry.valid_end) { + r->ek.last_req.val[r->ek.last_req.len].lr_type = LR_ACCT_EXPTIME; + r->ek.last_req.val[r->ek.last_req.len].lr_value = *r->client->entry.valid_end; + ++r->ek.last_req.len; } - if (ek.last_req.len == 0) { - ek.last_req.val[ek.last_req.len].lr_type = LR_NONE; - ek.last_req.val[ek.last_req.len].lr_value = 0; - ++ek.last_req.len; + if (r->ek.last_req.len == 0) { + r->ek.last_req.val[r->ek.last_req.len].lr_type = LR_NONE; + r->ek.last_req.val[r->ek.last_req.len].lr_value = 0; + ++r->ek.last_req.len; } - ek.nonce = b->nonce; - if (client->entry.valid_end || client->entry.pw_end) { - ALLOC(ek.key_expiration); - if (client->entry.valid_end) { - if (client->entry.pw_end) - *ek.key_expiration = min(*client->entry.valid_end, - *client->entry.pw_end); + r->ek.nonce = b->nonce; + if (r->client->entry.valid_end || r->client->entry.pw_end) { + ALLOC(r->ek.key_expiration); + if (r->client->entry.valid_end) { + if (r->client->entry.pw_end) + *r->ek.key_expiration = min(*r->client->entry.valid_end, + *r->client->entry.pw_end); else - *ek.key_expiration = *client->entry.valid_end; + *r->ek.key_expiration = *r->client->entry.valid_end; } else - *ek.key_expiration = *client->entry.pw_end; + *r->ek.key_expiration = *r->client->entry.pw_end; } else - ek.key_expiration = NULL; - ek.flags = et.flags; - ek.authtime = et.authtime; - if (et.starttime) { - ALLOC(ek.starttime); - *ek.starttime = *et.starttime; + r->ek.key_expiration = NULL; + r->ek.flags = r->et.flags; + r->ek.authtime = r->et.authtime; + if (r->et.starttime) { + ALLOC(r->ek.starttime); + *r->ek.starttime = *r->et.starttime; } - ek.endtime = et.endtime; - if (et.renew_till) { - ALLOC(ek.renew_till); - *ek.renew_till = *et.renew_till; + r->ek.endtime = r->et.endtime; + if (r->et.renew_till) { + ALLOC(r->ek.renew_till); + *r->ek.renew_till = *r->et.renew_till; } - copy_Realm(&rep.ticket.realm, &ek.srealm); - copy_PrincipalName(&rep.ticket.sname, &ek.sname); - if(et.caddr){ - ALLOC(ek.caddr); - copy_HostAddresses(et.caddr, ek.caddr); + copy_Realm(&rep.ticket.realm, &r->ek.srealm); + copy_PrincipalName(&rep.ticket.sname, &r->ek.sname); + if(r->et.caddr){ + ALLOC(r->ek.caddr); + copy_HostAddresses(r->et.caddr, r->ek.caddr); } -#if PKINIT - if (pkp) { - e_text = "Failed to build PK-INIT reply"; - ret = _kdc_pk_mk_pa_reply(context, config, pkp, client, - sessionetype, req, req_buffer, - &reply_key, &et.key, rep.padata); - if (ret) - goto out; - ret = _kdc_add_inital_verified_cas(context, - config, - pkp, - &et); - if (ret) - goto out; + /* + * Check and session and reply keys + */ - } else -#endif - { - ret = krb5_generate_random_keyblock(context, sessionetype, &et.key); + if (r->session_key.keytype == ETYPE_NULL) { + ret = krb5_generate_random_keyblock(context, r->sessionetype, &r->session_key); if (ret) goto out; } - if (reply_key == NULL) { - e_text = "Client have no reply key"; + if (r->reply_key.keytype == ETYPE_NULL) { + _kdc_set_e_text(r, "Client have no reply key"); ret = KRB5KDC_ERR_CLIENT_NOTYET; goto out; } - ret = copy_EncryptionKey(&et.key, &ek.key); + ret = copy_EncryptionKey(&r->session_key, &r->et.key); if (ret) goto out; - /* Add signing of alias referral */ + ret = copy_EncryptionKey(&r->session_key, &r->ek.key); + if (ret) + goto out; + + /* + * Add signing of alias referral + */ + if (f.canonicalize) { PA_ClientCanonicalized canon; krb5_data data; @@ -1709,7 +2038,7 @@ _kdc_as_rep(krb5_context context, memset(&canon, 0, sizeof(canon)); canon.names.requested_name = *b->cname; - canon.names.mapped_name = client->entry.principal->name; + canon.names.mapped_name = r->client->entry.principal->name; ASN1_MALLOC_ENCODE(PA_ClientCanonicalizedNames, data.data, data.length, &canon.names, &len, ret); @@ -1719,7 +2048,7 @@ _kdc_as_rep(krb5_context context, krb5_abortx(context, "internal asn.1 error"); /* sign using "returned session key" */ - ret = krb5_crypto_init(context, &et.key, 0, &cryptox); + ret = krb5_crypto_init(context, &r->et.key, 0, &cryptox); if (ret) { free(data.data); goto out; @@ -1744,113 +2073,137 @@ _kdc_as_rep(krb5_context context, pa.padata_type = KRB5_PADATA_CLIENT_CANONICALIZED; pa.padata_value = data; - ret = add_METHOD_DATA(rep.padata, &pa); + ret = add_METHOD_DATA(&r->outpadata, &pa); free(data.data); if (ret) goto out; } - if (rep.padata->len == 0) { - free(rep.padata); - rep.padata = NULL; + if (r->outpadata.len) { + + ALLOC(rep.padata); + if (rep.padata == NULL) { + ret = ENOMEM; + goto out; + } + ret = copy_METHOD_DATA(&r->outpadata, rep.padata); + if (ret) + goto out; } /* Add the PAC */ if (send_pac_p(context, req)) { - krb5_pac p = NULL; - krb5_data data; - - ret = _kdc_pac_generate(context, client, &p); - if (ret) { - kdc_log(context, config, 0, "PAC generation failed for -- %s", - client_name); - goto out; - } - if (p != NULL) { - ret = _krb5_pac_sign(context, p, et.authtime, - client->entry.principal, - &skey->key, /* Server key */ - &skey->key, /* FIXME: should be krbtgt key */ - &data); - krb5_pac_free(context, p); - if (ret) { - kdc_log(context, config, 0, "PAC signing failed for -- %s", - client_name); - goto out; - } - - ret = _kdc_tkt_add_if_relevant_ad(context, &et, - KRB5_AUTHDATA_WIN2K_PAC, - &data); - krb5_data_free(&data); - if (ret) - goto out; - } + generate_pac(r, skey); } - _kdc_log_timestamp(context, config, "AS-REQ", et.authtime, et.starttime, - et.endtime, et.renew_till); + _kdc_log_timestamp(context, config, "AS-REQ", r->et.authtime, r->et.starttime, + r->et.endtime, r->et.renew_till); /* do this as the last thing since this signs the EncTicketPart */ ret = _kdc_add_KRB5SignedPath(context, config, - server, + r->server, setype, - client->entry.principal, + r->client->entry.principal, NULL, NULL, - &et); + &r->et); if (ret) goto out; - log_as_req(context, config, reply_key->keytype, setype, b); + log_as_req(context, config, r->reply_key.keytype, setype, b); + + /* + * Add REQ_ENC_PA_REP if client supports it + */ + + i = 0; + pa = _kdc_find_padata(req, &i, KRB5_PADATA_REQ_ENC_PA_REP); + if (pa) { + + ret = add_enc_pa_rep(r); + if (ret) { + const char *msg = krb5_get_error_message(r->context, ret); + _kdc_r_log(r, 0, "add_enc_pa_rep failed: %s: %d", msg, ret); + krb5_free_error_message(r->context, msg); + goto out; + } + } + + /* + * + */ ret = _kdc_encode_reply(context, config, - &rep, &et, &ek, setype, server->entry.kvno, - &skey->key, client->entry.kvno, - reply_key, 0, &e_text, reply); - free_EncTicketPart(&et); - free_EncKDCRepPart(&ek); + r->armor_crypto, req->req_body.nonce, + &rep, &r->et, &r->ek, setype, r->server->entry.kvno, + &skey->key, r->client->entry.kvno, + &r->reply_key, 0, &r->e_text, reply); if (ret) goto out; - /* */ + /* + * Check if message too large + */ if (datagram_reply && reply->length > config->max_datagram_reply_length) { krb5_data_free(reply); ret = KRB5KRB_ERR_RESPONSE_TOO_BIG; - e_text = "Reply packet too large"; + _kdc_set_e_text(r, "Reply packet too large"); } out: free_AS_REP(&rep); - if(ret != 0 && ret != HDB_ERR_NOT_FOUND_HERE){ - krb5_mk_error(context, - ret, - e_text, - (e_data.data ? &e_data : NULL), - client_princ, - server_princ, - NULL, - NULL, - reply); - ret = 0; + + /* + * In case of a non proxy error, build an error message. + */ + if(ret != 0 && ret != HDB_ERR_NOT_FOUND_HERE) { + ret = _kdc_fast_mk_error(context, r, + &error_method, + r->armor_crypto, + &req->req_body, + ret, r->e_text, + r->client_princ, r->server_princ, + NULL, NULL, + reply); + if (ret) + goto out2; } -#ifdef PKINIT - if (pkp) - _kdc_pk_free_client_param(context, pkp); -#endif - if (e_data.data) - free(e_data.data); - if (client_princ) - krb5_free_principal(context, client_princ); - free(client_name); - if (server_princ) - krb5_free_principal(context, server_princ); - free(server_name); - if(client) - _kdc_free_ent(context, client); - if(server) - _kdc_free_ent(context, server); +out2: + free_EncTicketPart(&r->et); + free_EncKDCRepPart(&r->ek); + free_KDCFastState(&r->fast); + + if (error_method.len) + free_METHOD_DATA(&error_method); + if (r->outpadata.len) + free_METHOD_DATA(&r->outpadata); + if (r->client_princ) { + krb5_free_principal(context, r->client_princ); + r->client_princ = NULL; + } + if (r->client_name) { + free(r->client_name); + r->client_name = NULL; + } + if (r->server_princ){ + krb5_free_principal(context, r->server_princ); + r->server_princ = NULL; + } + if (r->server_name) { + free(r->server_name); + r->server_name = NULL; + } + if (r->client) + _kdc_free_ent(context, r->client); + if (r->server) + _kdc_free_ent(context, r->server); + if (r->armor_crypto) { + krb5_crypto_destroy(r->context, r->armor_crypto); + r->armor_crypto = NULL; + } + krb5_free_keyblock_contents(r->context, &r->reply_key); + krb5_free_keyblock_contents(r->context, &r->session_key); return ret; } diff --git a/kdc/krb5tgs.c b/kdc/krb5tgs.c index 903aaaa91..1d89adf49 100644 --- a/kdc/krb5tgs.c +++ b/kdc/krb5tgs.c @@ -987,7 +987,7 @@ tgs_make_reply(krb5_context context, CAST session key. Should the DES3 etype be added to the etype list, even if we don't want a session key with DES3? */ - ret = _kdc_encode_reply(context, config, + ret = _kdc_encode_reply(context, config, NULL, 0 &rep, &et, &ek, serverkey->keytype, kvno, serverkey, 0, replykey, rk_is_subkey, @@ -2362,6 +2362,14 @@ _kdc_tgs_rep(krb5_context context, goto out; } + { + int i = 0; + const PA_DATA *pa = _kdc_find_padata(req, &i, KRB5_PADATA_FX_FAST); + if (pa) + kdc_log(context, config, 10, "Got TGS FAST request"); + } + + ret = tgs_build_reply(context, config, req, @@ -2392,17 +2400,22 @@ _kdc_tgs_rep(krb5_context context, out: if (replykey) krb5_free_keyblock(context, replykey); + if(ret && ret != HDB_ERR_NOT_FOUND_HERE && data->data == NULL){ - krb5_mk_error(context, - ret, - NULL, - NULL, - NULL, - NULL, - csec, - cusec, - data); - ret = 0; + /* XXX add fast wrapping on the error */ + METHOD_DATA error_method = { 0, NULL }; + + + kdc_log(context, config, 10, "tgs-req: sending error: %d to client", ret); + ret = _kdc_fast_mk_error(context, NULL, + &error_method, + NULL, + NULL, + ret, NULL, + NULL, NULL, + csec, cusec, + data); + free_METHOD_DATA(&error_method); } free(csec); free(cusec); diff --git a/kdc/pkinit.c b/kdc/pkinit.c index 452515d19..f56b09c40 100644 --- a/kdc/pkinit.c +++ b/kdc/pkinit.c @@ -1237,7 +1237,7 @@ _kdc_pk_mk_pa_reply(krb5_context context, krb5_enctype sessionetype, const KDC_REQ *req, const krb5_data *req_buffer, - krb5_keyblock **reply_key, + krb5_keyblock *reply_key, krb5_keyblock *sessionkey, METHOD_DATA *md) { @@ -1563,7 +1563,7 @@ out: hx509_cert_free(kdc_cert); if (ret == 0) - *reply_key = &cp->reply_key; + ret = krb5_copy_keyblock_contents(context, &cp->reply_key, reply_key); return ret; } diff --git a/kdc/process.c b/kdc/process.c index 6f3691580..c6535bb22 100644 --- a/kdc/process.c +++ b/kdc/process.c @@ -57,19 +57,25 @@ kdc_as_req(krb5_context context, int datagram_reply, int *claim) { + struct kdc_request_desc r; krb5_error_code ret; - KDC_REQ req; size_t len; - ret = decode_AS_REQ(req_buffer->data, req_buffer->length, &req, &len); + memset(&r, 0, sizeof(r)); + + ret = decode_AS_REQ(req_buffer->data, req_buffer->length, &r.req, &len); if (ret) return ret; + r.context = context; + r.config = config; + r.request.data = req_buffer->data; + r.request.length = req_buffer->length; + *claim = 1; - ret = _kdc_as_rep(context, config, &req, req_buffer, - reply, from, addr, datagram_reply); - free_AS_REQ(&req); + ret = _kdc_as_rep(&r, reply, from, addr, datagram_reply); + free_AS_REQ(&r.req); return ret; } diff --git a/kdc/windc.c b/kdc/windc.c index ba87abb7c..7cd64ca9f 100644 --- a/kdc/windc.c +++ b/kdc/windc.c @@ -111,7 +111,7 @@ _kdc_check_access(krb5_context context, hdb_entry_ex *client_ex, const char *client_name, hdb_entry_ex *server_ex, const char *server_name, KDC_REQ *req, - krb5_data *e_data) + METHOD_DATA *method_data) { if (windcft == NULL) return kdc_check_flags(context, config, @@ -119,9 +119,9 @@ _kdc_check_access(krb5_context context, server_ex, server_name, req->msg_type == krb_as_req); - return (windcft->client_access)(windcctx, - context, config, - client_ex, client_name, - server_ex, server_name, - req, e_data); + return (windcft->client_access)(windcctx, + context, config, + client_ex, client_name, + server_ex, server_name, + req, method_data); } diff --git a/kdc/windc_plugin.h b/kdc/windc_plugin.h index fa4ba434f..4401500e6 100644 --- a/kdc/windc_plugin.h +++ b/kdc/windc_plugin.h @@ -68,9 +68,9 @@ typedef krb5_error_code (*krb5plugin_windc_client_access)( void *, krb5_context, krb5_kdc_configuration *config, - hdb_entry_ex *, const char *, - hdb_entry_ex *, const char *, - KDC_REQ *, krb5_data *); + hdb_entry_ex *, const char *, + hdb_entry_ex *, const char *, + KDC_REQ *, METHOD_DATA *); #define KRB5_WINDC_PLUGIN_MINOR 6 diff --git a/kuser/kinit.c b/kuser/kinit.c index 0b3876dcc..e74a9147b 100644 --- a/kuser/kinit.c +++ b/kuser/kinit.c @@ -74,6 +74,7 @@ int pk_use_enckey = 0; static int canonicalize_flag = 0; static int enterprise_flag = 0; static int ok_as_delegate_flag = 0; +static char *fast_armor_cache_string = NULL; static int use_referrals_flag = 0; static int windows_flag = 0; #ifndef NO_NTLM @@ -187,6 +188,9 @@ static struct getargs args[] = { { "ok-as-delegate", 0, arg_flag, &ok_as_delegate_flag, NP_("honor ok-as-delegate on tickets", ""), NULL }, + { "fast-armor-cache", 0, arg_string, &fast_armor_cache_string, + NP_("use this credential cache as FAST armor cache", ""), "cache" }, + { "use-referrals", 0, arg_flag, &use_referrals_flag, NP_("only use referrals, no dns canalisation", ""), NULL }, @@ -360,6 +364,8 @@ get_new_tickets(krb5_context context, const char *renewstr = NULL; krb5_enctype *enctype = NULL; krb5_ccache tempccache; + krb5_keytab kt = NULL; + krb5_init_creds_context ctx; #ifndef NO_NTLM struct ntlm_buf ntlmkey; memset(&ntlmkey, 0, sizeof(ntlmkey)); @@ -498,32 +504,43 @@ get_new_tickets(krb5_context context, etype_str.num_strings); } + ret = krb5_init_creds_init(context, principal, krb5_prompter_posix, NULL, start_time, opt, &ctx); + if (ret) + krb5_err(context, 1, ret, "krb5_init_creds_init"); + + if (server_str) { + ret = krb5_init_creds_set_service(context, ctx, server_str); + if (ret) + krb5_err(context, 1, ret, "krb5_init_creds_set_service"); + } + + if (fast_armor_cache_string) { + krb5_ccache fastid; + + ret = krb5_cc_resolve(context, fast_armor_cache_string, &fastid); + if (ret) + krb5_err(context, 1, ret, "krb5_cc_resolve(FAST cache)"); + + ret = krb5_init_creds_set_fast_ccache(context, ctx, fastid); + if (ret) + krb5_err(context, 1, ret, "krb5_init_creds_set_fast_ccache"); + } + if(use_keytab || keytab_str) { - krb5_keytab kt; + if(keytab_str) ret = krb5_kt_resolve(context, keytab_str, &kt); else ret = krb5_kt_default(context, &kt); if (ret) - krb5_err (context, 1, ret, "resolving keytab"); - ret = krb5_get_init_creds_keytab (context, - &cred, - principal, - kt, - start_time, - server_str, - opt); - krb5_kt_close(context, kt); + krb5_err(context, 1, ret, "resolving keytab"); + + ret = krb5_init_creds_set_keytab(context, ctx, kt); + if (ret) + krb5_err(context, 1, ret, "krb5_init_creds_set_keytab"); + } else if (pk_user_id || ent_user_id || anonymous_flag) { - ret = krb5_get_init_creds_password (context, - &cred, - principal, - passwd, - krb5_prompter_posix, - NULL, - start_time, - server_str, - opt); + } else if (!interactive) { krb5_warnx(context, "Not interactive, failed to get initial ticket"); krb5_get_init_creds_opt_free(context, opt); @@ -539,23 +556,20 @@ get_new_tickets(krb5_context context, if (UI_UTIL_read_pw_string(passwd, sizeof(passwd)-1, prompt, 0)){ memset(passwd, 0, sizeof(passwd)); - exit(1); + errx(1, "failed to read password"); } free (prompt); } - - ret = krb5_get_init_creds_password (context, - &cred, - principal, - passwd, - krb5_prompter_posix, - NULL, - start_time, - server_str, - opt); + if (passwd[0]) { + ret = krb5_init_creds_set_password(context, ctx, passwd); + if (ret) + krb5_err(context, 1, ret, "krb5_init_creds_set_password"); + } } - krb5_get_init_creds_opt_free(context, opt); + + ret = krb5_init_creds_get(context, ctx); + #ifndef NO_NTLM if (ntlm_domain && passwd[0]) heim_ntlm_nt_key(passwd, &ntlmkey); @@ -579,6 +593,8 @@ get_new_tickets(krb5_context context, krb5_err(context, 1, ret, "krb5_get_init_creds"); } + krb5_process_last_request(context, opt, ctx); + if(ticket_life != 0) { if(abs(cred.times.endtime - cred.times.starttime - ticket_life) > 30) { char life[64]; @@ -603,15 +619,11 @@ get_new_tickets(krb5_context context, if (ret) krb5_err (context, 1, ret, "krb5_cc_new_unique"); - ret = krb5_cc_initialize (context, tempccache, cred.client); + ret = krb5_init_creds_store(context, ctx, tempccache); if (ret) - krb5_err (context, 1, ret, "krb5_cc_initialize"); + krb5_err(context, 1, ret, "krb5_init_creds_store"); - ret = krb5_cc_store_cred (context, tempccache, &cred); - if (ret) - krb5_err (context, 1, ret, "krb5_cc_store_cred"); - - krb5_free_cred_contents (context, &cred); + krb5_init_creds_free(context, ctx); ret = krb5_cc_move(context, tempccache, ccache); if (ret) @@ -640,7 +652,10 @@ get_new_tickets(krb5_context context, krb5_cc_set_config(context, ccache, NULL, "realm-config", &data); } + krb5_get_init_creds_opt_free(context, opt); + if (kt) + krb5_kt_close(context, kt); if (enctype) free(enctype); diff --git a/lib/asn1/gen.c b/lib/asn1/gen.c index 2194b329c..afe23cbf7 100644 --- a/lib/asn1/gen.c +++ b/lib/asn1/gen.c @@ -888,7 +888,7 @@ define_type (int level, const char *name, const char *basename, Type *t, int typ fprintf(headerfile, "heim_octet_string _save;\n"); } space(level + 1); - fprintf (headerfile, "enum {\n"); + fprintf (headerfile, "enum %s_enum {\n", newbasename); m = have_ellipsis(t); if (m) { space(level + 2); diff --git a/lib/asn1/krb5.asn1 b/lib/asn1/krb5.asn1 index 265b5f024..728fa2488 100644 --- a/lib/asn1/krb5.asn1 +++ b/lib/asn1/krb5.asn1 @@ -63,6 +63,8 @@ EXPORTS PA-ServerReferralData, PA-SvrReferralData, PADATA-TYPE, + PA-FX-FAST-REQUEST, + PA-FX-FAST-REPLY, Principal, PrincipalName, Principals, @@ -72,7 +74,13 @@ EXPORTS Ticket, TicketFlags, TransitedEncoding, - TypedData + TypedData, + KrbFastResponse, + KrbFastFinished, + KrbFastReq, + KrbFastArmor, + KDCFastState, + KDCFastCookie ; NAME-TYPE ::= INTEGER { @@ -771,12 +779,12 @@ PA-ServerReferralData ::= SEQUENCE { FastOptions ::= BIT STRING { reserved(0), hide-client-names(1), - kdc-follow--referrals(16) + kdc-follow-referrals(16) } KrbFastReq ::= SEQUENCE { fast-options [0] FastOptions, - padata [1] SEQUENCE OF PA-DATA, + padata [1] METHOD-DATA, req-body [2] KDC-REQ-BODY, ... } @@ -803,15 +811,15 @@ KrbFastFinished ::= SEQUENCE { usec [1] krb5int32, crealm [2] Realm, cname [3] PrincipalName, - checksum [4] Checksum, - ticket-checksum [5] Checksum, + ticket-checksum [4] Checksum, ... } KrbFastResponse ::= SEQUENCE { - padata [0] SEQUENCE OF PA-DATA, - rep-key [1] EncryptionKey OPTIONAL, - finished [2] KrbFastFinished OPTIONAL, + padata [0] METHOD-DATA, + strengthen-key [1] EncryptionKey OPTIONAL, + finished [2] KrbFastFinished OPTIONAL, + nonce [3] krb5uint32, ... } @@ -825,6 +833,26 @@ PA-FX-FAST-REPLY ::= CHOICE { ... } +KDCFastFlags ::= BIT STRING { + use_reply_key(0), + reply_key_used(1), + reply_key_replaced(2), + kdc_verfied(3) +} + +-- KDCFastState is stored in FX_COOKIE +KDCFastState ::= SEQUENCE { + flags [0] KDCFastFlags, + expiration [1] GeneralizedTime, + fast-state [2] METHOD-DATA, + expected-pa-types [3] SEQUENCE OF PADATA-TYPE OPTIONAL +} + +KDCFastCookie ::= SEQUENCE { + version [0] UTF8String, + cookie [1] EncryptedData +} + END -- etags -r '/\([A-Za-z][-A-Za-z0-9]*\).*::=/\1/' k5.asn1 diff --git a/lib/krb5/Makefile.am b/lib/krb5/Makefile.am index 0e4b87ffb..07d56715d 100644 --- a/lib/krb5/Makefile.am +++ b/lib/krb5/Makefile.am @@ -147,6 +147,7 @@ dist_libkrb5_la_SOURCES = \ error_string.c \ expand_hostname.c \ expand_path.c \ + fast.c \ fcache.c \ free.c \ free_host_realm.c \ diff --git a/lib/krb5/crypto-null.c b/lib/krb5/crypto-null.c index b647a6d10..e8ed19c6d 100644 --- a/lib/krb5/crypto-null.c +++ b/lib/krb5/crypto-null.c @@ -64,7 +64,7 @@ struct _krb5_checksum_type _krb5_checksum_none = { "none", 1, 0, - 0, + F_DISABLED, NONE_checksum, NULL }; diff --git a/lib/krb5/fast.c b/lib/krb5/fast.c new file mode 100644 index 000000000..6da482d41 --- /dev/null +++ b/lib/krb5/fast.c @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2011 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "krb5_locl.h" + + +krb5_error_code +_krb5_fast_cf2(krb5_context context, + krb5_keyblock *key1, + const char *pepper1, + krb5_keyblock *key2, + const char *pepper2, + krb5_keyblock *armorkey, + krb5_crypto *armor_crypto) +{ + krb5_crypto crypto1, crypto2; + krb5_data pa1, pa2; + krb5_error_code ret; + + ret = krb5_crypto_init(context, key1, 0, &crypto1); + if (ret) + return ret; + + ret = krb5_crypto_init(context, key2, 0, &crypto2); + if (ret) { + krb5_crypto_destroy(context, crypto1); + return ret; + } + + pa1.data = rk_UNCONST(pepper1); + pa1.length = strlen(pepper1); + pa2.data = rk_UNCONST(pepper2); + pa2.length = strlen(pepper2); + + ret = krb5_crypto_fx_cf2(context, crypto1, crypto2, &pa1, &pa2, + key1->keytype, armorkey); + krb5_crypto_destroy(context, crypto1); + krb5_crypto_destroy(context, crypto2); + if (ret) + return ret; + + if (armor_crypto) { + ret = krb5_crypto_init(context, armorkey, 0, armor_crypto); + if (ret) + krb5_free_keyblock_contents(context, armorkey); + } + + return ret; +} + +krb5_error_code +_krb5_fast_armor_key(krb5_context context, + krb5_keyblock *subkey, + krb5_keyblock *sessionkey, + krb5_keyblock *armorkey, + krb5_crypto *armor_crypto) +{ + return _krb5_fast_cf2(context, + subkey, + "subkeyarmor", + sessionkey, + "ticketarmor", + armorkey, + armor_crypto); +} diff --git a/lib/krb5/get_cred.c b/lib/krb5/get_cred.c index 8c8fe820c..ff95e1052 100644 --- a/lib/krb5/get_cred.c +++ b/lib/krb5/get_cred.c @@ -544,6 +544,7 @@ get_cred_kdc(krb5_context context, &krbtgt->addresses, nonce, eflags, + NULL, decrypt_tkt_with_subkey, subkey); out2: diff --git a/lib/krb5/get_in_tkt.c b/lib/krb5/get_in_tkt.c index 41618b961..c4b16c9a2 100644 --- a/lib/krb5/get_in_tkt.c +++ b/lib/krb5/get_in_tkt.c @@ -510,6 +510,7 @@ krb5_get_in_cred(krb5_context context, NULL, nonce, flags, + NULL, decrypt_proc, decryptarg); } diff --git a/lib/krb5/init_creds_pw.c b/lib/krb5/init_creds_pw.c index 6c874126a..118ba3e50 100644 --- a/lib/krb5/init_creds_pw.c +++ b/lib/krb5/init_creds_pw.c @@ -76,7 +76,20 @@ typedef struct krb5_get_init_creds_ctx { void *prompter_data; struct pa_info_data *ppaid; - + struct fast_state { + enum PA_FX_FAST_REQUEST_enum type; + unsigned int flags; +#define KRB5_FAST_REPLY_KEY_USE_TO_ENCRYPT_THE_REPLY 1 +#define KRB5_FAST_REPLY_KEY_USE_IN_TRANSACTION 2 +#define KRB5_FAST_KDC_REPLY_KEY_REPLACED 4 +#define KRB5_FAST_REPLY_REPLY_VERIFED 8 +#define KRB5_FAST_STRONG 16 +#define KRB5_FAST_EXPECTED 32 + krb5_keyblock *reply_key; + krb5_ccache armor_ccache; + krb5_crypto armor_crypto; + krb5_keyblock armor_key; + } fast_state; } krb5_get_init_creds_ctx; @@ -140,6 +153,15 @@ free_init_creds_ctx(krb5_context context, krb5_init_creds_context ctx) memset(ctx->password, 0, strlen(ctx->password)); free(ctx->password); } + /* + * FAST state + */ + if (ctx->fast_state.armor_ccache) + krb5_cc_close(context, ctx->fast_state.armor_ccache); + if (ctx->fast_state.armor_crypto) + krb5_crypto_destroy(context, ctx->fast_state.armor_crypto); + krb5_free_keyblock_contents(context, &ctx->fast_state.armor_key); + krb5_data_free(&ctx->req_buffer); krb5_free_cred_contents(context, &ctx->cred); free_METHOD_DATA(&ctx->md); @@ -251,10 +273,10 @@ report_expiration (krb5_context context, * @param ctx The krb5_init_creds_context check for expiration. */ -static krb5_error_code -process_last_request(krb5_context context, - krb5_get_init_creds_opt *options, - krb5_init_creds_context ctx) +krb5_error_code +krb5_process_last_request(krb5_context context, + krb5_get_init_creds_opt *options, + krb5_init_creds_context ctx) { krb5_const_realm realm; LastReq *lr; @@ -1237,6 +1259,10 @@ process_pa_data_to_md(krb5_context context, pa_data_add_pac_request(context, ctx, *out_md); + ret = krb5_padata_add(context, *out_md, KRB5_PADATA_REQ_ENC_PA_REP, NULL, 0); + if (ret) + return ret; + if ((*out_md)->len == 0) { free(*out_md); *out_md = NULL; @@ -1618,6 +1644,352 @@ krb5_init_creds_set_keyblock(krb5_context context, return 0; } +KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL +krb5_init_creds_set_fast_ccache(krb5_context context, + krb5_init_creds_context ctx, + krb5_ccache fast_ccache) +{ + ctx->fast_state.armor_ccache = fast_ccache; + return 0; +} + +/* + * FAST + */ + +static krb5_error_code +check_fast(krb5_context context, struct fast_state *state) +{ + if (state->flags & KRB5_FAST_EXPECTED) { + krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED, + "Expected FAST, but no FAST " + "was in the response from the KDC"); + return KRB5KRB_AP_ERR_MODIFIED; + } + return 0; +} + + +static krb5_error_code +fast_unwrap_as_rep(krb5_context context, int32_t nonce, + krb5_data *chksumdata, + struct fast_state *state, AS_REP *rep) +{ + PA_FX_FAST_REPLY fxfastrep; + KrbFastResponse fastrep; + krb5_error_code ret; + PA_DATA *pa = NULL; + int idx = 0; + + if (state->armor_crypto == NULL || rep->padata == NULL) + return check_fast(context, state); + + /* find PA_FX_FAST_REPLY */ + + pa = krb5_find_padata(rep->padata->val, rep->padata->len, + KRB5_PADATA_FX_FAST, &idx); + if (pa == NULL) + return check_fast(context, state); + + memset(&fxfastrep, 0, sizeof(fxfastrep)); + memset(&fastrep, 0, sizeof(fastrep)); + + ret = decode_PA_FX_FAST_REPLY(pa->padata_value.data, pa->padata_value.length, &fxfastrep, NULL); + if (ret) + return ret; + + if (fxfastrep.element == choice_PA_FX_FAST_REPLY_armored_data) { + krb5_data data; + ret = krb5_decrypt_EncryptedData(context, + state->armor_crypto, + KRB5_KU_FAST_REP, + &fxfastrep.u.armored_data.enc_fast_rep, + &data); + if (ret) + goto out; + + ret = decode_KrbFastResponse(data.data, data.length, &fastrep, NULL); + krb5_data_free(&data); + if (ret) + goto out; + + } else { + ret = KRB5KDC_ERR_PREAUTH_FAILED; + goto out; + } + + free_METHOD_DATA(rep->padata); + ret = copy_METHOD_DATA(&fastrep.padata, rep->padata); + if (ret) + goto out; + + if (fastrep.strengthen_key) { + krb5_keyblock result; + + ret = _krb5_fast_cf2(context, + fastrep.strengthen_key, + "strengthenkey", + state->reply_key, + "replykey", + &result, + NULL); + if (ret) + goto out; + + krb5_free_keyblock_contents(context, state->reply_key); + *state->reply_key = result; + } + + if (nonce != fastrep.nonce) { + ret = KRB5KDC_ERR_PREAUTH_FAILED; + goto out; + } + if (fastrep.finished) { + PrincipalName cname; + krb5_realm crealm = NULL; + + if (chksumdata == NULL) { + ret = KRB5KDC_ERR_PREAUTH_FAILED; + goto out; + } + + ret = krb5_verify_checksum(context, state->armor_crypto, + KRB5_KU_FAST_FINISHED, + chksumdata->data, chksumdata->length, + &fastrep.finished->ticket_checksum); + if (ret) + goto out; + + /* update */ + ret = copy_Realm(&fastrep.finished->crealm, &crealm); + if (ret) + goto out; + free_Realm(&rep->crealm); + rep->crealm = crealm; + + ret = copy_PrincipalName(&fastrep.finished->cname, &cname); + if (ret) + goto out; + free_PrincipalName(&rep->cname); + rep->cname = cname; + +#if 0 /* store authenticated checksum as kdc-offset */ + fastrep->finished.timestamp; + fastrep->finished.usec = 0; +#endif + + } else if (chksumdata) { + /* expected fastrep.finish but didn't get it */ + ret = KRB5KDC_ERR_PREAUTH_FAILED; + } + + out: + free_PA_FX_FAST_REPLY(&fxfastrep); + + return ret; +} + +static krb5_error_code +fast_unwrap_error(krb5_context context, struct fast_state *state, KRB_ERROR *error) +{ + if (state->armor_crypto == NULL) + return check_fast(context, state); + + return 0; +} + +static krb5_error_code +make_fast_ap_fxarmor(krb5_context context, + struct fast_state *state, + KrbFastArmor **armor) +{ + KrbFastArmor *fxarmor = NULL; + krb5_auth_context auth_context = NULL; + krb5_creds cred, *credp = NULL; + krb5_error_code ret; + + ALLOC(fxarmor, 1); + if (fxarmor == NULL) { + ret = ENOMEM; + goto out; + } + + fxarmor->armor_type = 1; + + memset(&cred, 0, sizeof(cred)); + + ret = krb5_auth_con_init (context, &auth_context); + if (ret) + goto out; + + ret = krb5_cc_get_principal(context, state->armor_ccache, &cred.client); + if (ret) + goto out; + + ret = krb5_make_principal(context, &cred.server, + cred.client->realm, + KRB5_TGS_NAME, + cred.client->realm, + NULL); + if (ret) { + krb5_free_principal(context, cred.client); + goto out; + } + + ret = krb5_get_credentials(context, 0, state->armor_ccache, &cred, &credp); + krb5_free_principal(context, cred.server); + krb5_free_principal(context, cred.client); + if (ret) + goto out; + + ret = krb5_mk_req_extended(context, + &auth_context, + AP_OPTS_USE_SUBKEY, + NULL, + credp, + &fxarmor->armor_value); + krb5_free_creds(context, credp); + if (ret) + goto out; + + if (state->armor_crypto) + krb5_crypto_destroy(context, state->armor_crypto); + krb5_free_keyblock_contents(context, &state->armor_key); + + ret = _krb5_fast_armor_key(context, + auth_context->local_subkey, + auth_context->keyblock, + &state->armor_key, + &state->armor_crypto); + if (ret) + goto out; + + *armor = fxarmor; + fxarmor = NULL; + out: + if (fxarmor) + free_KrbFastArmor(fxarmor); + return ret; +} + + +static krb5_error_code +fast_wrap_req(krb5_context context, struct fast_state *state, KDC_REQ *req) +{ + KrbFastArmor *fxarmor = NULL; + PA_FX_FAST_REQUEST fxreq; + krb5_error_code ret; + KrbFastReq fastreq; + krb5_data data; + size_t size; + + memset(&fxreq, 0, sizeof(fxreq)); + memset(&fastreq, 0, sizeof(fastreq)); + krb5_data_zero(&data); + + if (state->armor_crypto == NULL) { + if (state->armor_ccache) { + /* + * Instead of keeping state in FX_COOKIE in the KDC, we + * rebuild a new armor key for every request, because this + * is what the MIT KDC expect and RFC6113 is vage about + * what the behavior should be. + */ + state->type = choice_PA_FX_FAST_REQUEST_armored_data; + } else { + return check_fast(context, state); + } + } + + state->flags |= KRB5_FAST_EXPECTED; + + fastreq.fast_options.hide_client_names = 1; + + ret = copy_KDC_REQ_BODY(&req->req_body, &fastreq.req_body); + free_KDC_REQ_BODY(&req->req_body); + + req->req_body.realm = strdup(KRB5_ANON_REALM); + ALLOC(req->req_body.cname, 1); + req->req_body.cname->name_type = KRB5_NT_PRINCIPAL; + ALLOC(req->req_body.cname->name_string.val, 2); + req->req_body.cname->name_string.len = 2; + req->req_body.cname->name_string.val[0] = strdup(KRB5_WELLKNOWN_NAME); + req->req_body.cname->name_string.val[1] = strdup(KRB5_ANON_NAME); + + if (req->padata) { + ret = copy_METHOD_DATA(req->padata, &fastreq.padata); + free_METHOD_DATA(req->padata); + } else { + ALLOC(req->padata, 1); + } + + + ASN1_MALLOC_ENCODE(KrbFastReq, data.data, data.length, &fastreq, &size, ret); + if (ret) + goto out; + heim_assert(data.length == size, "ASN.1 internal error"); + + fxreq.element = state->type; + + if (state->type == choice_PA_FX_FAST_REQUEST_armored_data) { + size_t len; + void *buf; + + ret = make_fast_ap_fxarmor(context, state, &fxreq.u.armored_data.armor); + if (ret) + goto out; + + heim_assert(state->armor_crypto != NULL, "FAST armor key missing when FAST started"); + + ASN1_MALLOC_ENCODE(KDC_REQ_BODY, buf, len, &req->req_body, &size, ret); + if (ret) + goto out; + heim_assert(len == size, "ASN.1 internal error"); + + ret = krb5_create_checksum(context, state->armor_crypto, + KRB5_KU_FAST_REQ_CHKSUM, 0, + buf, len, + &fxreq.u.armored_data.req_checksum); + free(buf); + if (ret) + goto out; + + ret = krb5_encrypt_EncryptedData(context, state->armor_crypto, + KRB5_KU_FAST_ENC, + data.data, + data.length, + 0, + &fxreq.u.armored_data.enc_fast_req); + krb5_data_free(&data); + + } else { + krb5_data_free(&data); + heim_assert(false, "unknown FAST type, internal error"); + } + + ASN1_MALLOC_ENCODE(PA_FX_FAST_REQUEST, data.data, data.length, &fxreq, &size, ret); + if (ret) + goto out; + heim_assert(data.length == size, "ASN.1 internal error"); + + + ret = krb5_padata_add(context, req->padata, KRB5_PADATA_FX_FAST, data.data, data.length); + if (ret) + goto out; + krb5_data_zero(&data); + + out: + free_PA_FX_FAST_REQUEST(&fxreq); + if (fxarmor) { + free_KrbFastArmor(fxarmor); + free(fxarmor); + } + krb5_data_free(&data); + + return ret; +} + + /** * The core loop if krb5_get_init_creds() function family. Create the * packets and have the caller send them off to the KDC. @@ -1650,6 +2022,7 @@ krb5_init_creds_step(krb5_context context, krb5_error_code ret; size_t len = 0; size_t size; + AS_REQ req2; krb5_data_zero(out); @@ -1684,8 +2057,27 @@ krb5_init_creds_step(krb5_context context, ret = decode_AS_REP(in->data, in->length, &rep.kdc_rep, &size); if (ret == 0) { - krb5_keyblock *key = NULL; unsigned eflags = EXTRACT_TICKET_AS_REQ | EXTRACT_TICKET_TIMESYNC; + krb5_data data; + + /* + * Unwrap AS-REP + */ + ASN1_MALLOC_ENCODE(Ticket, data.data, data.length, + &rep.kdc_rep.ticket, &size, ret); + if (ret) + goto out; + heim_assert(data.length == size, "ASN.1 internal error"); + + ret = fast_unwrap_as_rep(context, ctx->nonce, &data, + &ctx->fast_state, &rep.kdc_rep); + krb5_data_free(&data); + if (ret) + goto out; + + /* + * Now check and extract the ticket + */ if (ctx->flags.canonicalize) { eflags |= EXTRACT_TICKET_ALLOW_SERVER_MISMATCH; @@ -1695,7 +2087,8 @@ krb5_init_creds_step(krb5_context context, eflags |= EXTRACT_TICKET_ALLOW_CNAME_MISMATCH; ret = process_pa_data_to_key(context, ctx, &ctx->cred, - &ctx->as_req, &rep.kdc_rep, hostinfo, &key); + &ctx->as_req, &rep.kdc_rep, + hostinfo, &ctx->fast_state.reply_key); if (ret) { free_AS_REP(&rep.kdc_rep); goto out; @@ -1706,21 +2099,22 @@ krb5_init_creds_step(krb5_context context, ret = _krb5_extract_ticket(context, &rep, &ctx->cred, - key, + ctx->fast_state.reply_key, NULL, KRB5_KU_AS_REP_ENC_PART, NULL, ctx->nonce, eflags, + &ctx->req_buffer, NULL, NULL); - krb5_free_keyblock(context, key); - - *flags = 0; - if (ret == 0) ret = copy_EncKDCRepPart(&rep.enc_part, &ctx->enc_part); + krb5_free_keyblock(context, ctx->fast_state.reply_key); + ctx->fast_state.reply_key = NULL; + *flags = 0; + free_AS_REP(&rep.kdc_rep); free_EncASRepPart(&rep.enc_part); @@ -1741,6 +2135,17 @@ krb5_init_creds_step(krb5_context context, goto out; } + /* + * Unwrap KRB-ERROR + */ + ret = fast_unwrap_error(context, &ctx->fast_state, &ctx->error); + if (ret) + goto out; + + /* + * + */ + ret = krb5_error_from_rd_error(context, &ctx->error, &ctx->cred); _krb5_debug(context, 5, "krb5_get_init_creds: KRB-ERROR %d", ret); @@ -1821,11 +2226,23 @@ krb5_init_creds_step(krb5_context context, if (ret) goto out; + /* + * Wrap with FAST + */ + copy_AS_REQ(&ctx->as_req, &req2); + + ret = fast_wrap_req(context, &ctx->fast_state, &req2); + if (ret) { + free_AS_REQ(&req2); + goto out; + } + krb5_data_free(&ctx->req_buffer); ASN1_MALLOC_ENCODE(AS_REQ, ctx->req_buffer.data, ctx->req_buffer.length, - &ctx->as_req, &len, ret); + &req2, &len, ret); + free_AS_REQ(&req2); if (ret) goto out; if(len != ctx->req_buffer.length) @@ -1882,6 +2299,43 @@ krb5_init_creds_get_error(krb5_context context, return ret; } +/** + * + * @ingroup krb5_credential + */ + +krb5_error_code +krb5_init_creds_store(krb5_context context, + krb5_init_creds_context ctx, + krb5_ccache id) +{ + krb5_error_code ret; + + if (ctx->cred.client == NULL) { + ret = KRB5KDC_ERR_PREAUTH_REQUIRED; + krb5_set_error_message(context, ret, "init creds not completed yet"); + return ret; + } + + ret = krb5_cc_initialize(context, id, ctx->cred.client); + if (ret) + return ret; + + ret = krb5_cc_store_cred(context, id, &ctx->cred); + if (ret) + return ret; + + if (ctx->cred.flags.b.enc_pa_rep) { + krb5_data data = { 3, rk_UNCONST("yes") }; + ret = krb5_cc_set_config(context, id, ctx->cred.server, + "fast_avail", &data); + if (ret) + return ret; + } + + return ret; +} + /** * Free the krb5_init_creds_context allocated by krb5_init_creds_init(). * @@ -2016,7 +2470,7 @@ krb5_get_init_creds_password(krb5_context context, ret = krb5_init_creds_get(context, ctx); if (ret == 0) - process_last_request(context, options, ctx); + krb5_process_last_request(context, options, ctx); if (ret == KRB5KDC_ERR_KEY_EXPIRED && chpw == 0) { @@ -2091,7 +2545,7 @@ krb5_get_init_creds_keyblock(krb5_context context, ret = krb5_init_creds_get(context, ctx); if (ret == 0) - process_last_request(context, options, ctx); + krb5_process_last_request(context, options, ctx); out: if (ret == 0) @@ -2137,7 +2591,7 @@ krb5_get_init_creds_keytab(krb5_context context, ret = krb5_init_creds_get(context, ctx); if (ret == 0) - process_last_request(context, options, ctx); + krb5_process_last_request(context, options, ctx); out: if (ret == 0) diff --git a/lib/krb5/krb5.h b/lib/krb5/krb5.h index 84b3f0e22..ed6dc1052 100644 --- a/lib/krb5/krb5.h +++ b/lib/krb5/krb5.h @@ -274,14 +274,28 @@ typedef enum krb5_key_usage { /* Encryption type of the kdc session contribution in pk-init */ KRB5_KU_AS_REQ = 56, /* Checksum of over the AS-REQ send by the KDC in PA-REQ-ENC-PA-REP */ + KRB5_KU_FAST_REQ_CHKSUM = 50, + /* FAST armor checksum */ + KRB5_KU_FAST_ENC = 51, + /* FAST armor encryption */ + KRB5_KU_FAST_REP = 52, + /* FAST armor reply */ + KRB5_KU_FAST_FINISHED = 53, + /* FAST finished checksum */ + KRB5_KU_ENC_CHALLENGE_CLIENT = 54, + /* fast challange from client */ + KRB5_KU_ENC_CHALLENGE_KDC = 55, + /* fast challange from kdc */ KRB5_KU_DIGEST_ENCRYPT = -18, /* Encryption key usage used in the digest encryption field */ KRB5_KU_DIGEST_OPAQUE = -19, /* Checksum key usage used in the digest opaque field */ KRB5_KU_KRB5SIGNEDPATH = -21, /* Checksum key usage on KRB5SignedPath */ - KRB5_KU_CANONICALIZED_NAMES = -23 + KRB5_KU_CANONICALIZED_NAMES = -23, /* Checksum key usage on PA-CANONICALIZED */ + KRB5_KU_H5L_COOKIE = -25 + /* encrypted foo */ } krb5_key_usage; typedef krb5_key_usage krb5_keyusage; @@ -671,6 +685,8 @@ typedef EncAPRepPart krb5_ap_rep_enc_part; #define KRB5_TGS_NAME ("krbtgt") #define KRB5_WELLKNOWN_NAME ("WELLKNOWN") #define KRB5_ANON_NAME ("ANONYMOUS") +#define KRB5_ANON_REALM ("WELLKNOWN:ANONYMOUS") +#define KRB5_WELLKNOWN_ORG_H5L_REALM ("WELLKNOWN:ORG.H5L") #define KRB5_DIGEST_NAME ("digest") typedef enum { diff --git a/lib/krb5/ticket.c b/lib/krb5/ticket.c index 4845a93d9..979d5ffa6 100644 --- a/lib/krb5/ticket.c +++ b/lib/krb5/ticket.c @@ -592,7 +592,9 @@ check_client_referral(krb5_context context, return 0; noreferral: - if (krb5_principal_compare(context, requested, mapped) == FALSE) { + if (krb5_principal_compare(context, requested, mapped) == FALSE && + !rep->enc_part.flags.enc_pa_rep) + { krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED, N_("Not same client principal returned " "as requested", "")); @@ -656,6 +658,7 @@ _krb5_extract_ticket(krb5_context context, krb5_addresses *addrs, unsigned nonce, unsigned flags, + krb5_data *request, krb5_decrypt_proc decrypt_proc, krb5_const_pointer decryptarg) { @@ -674,6 +677,48 @@ _krb5_extract_ticket(krb5_context context, if (ret) goto out; + if (rep->enc_part.flags.enc_pa_rep && request) { + krb5_crypto crypto = NULL; + Checksum cksum; + PA_DATA *pa = NULL; + int idx = 0; + + _krb5_debug(context, 5, "processing enc-ap-rep"); + + if (rep->enc_part.encrypted_pa_data == NULL || + (pa = krb5_find_padata(rep->enc_part.encrypted_pa_data->val, + rep->enc_part.encrypted_pa_data->len, + KRB5_PADATA_REQ_ENC_PA_REP, + &idx)) == NULL) + { + _krb5_debug(context, 5, "KRB5_PADATA_REQ_ENC_PA_REP missing"); + ret = KRB5KRB_AP_ERR_MODIFIED; + goto out; + } + + ret = krb5_crypto_init(context, key, 0, &crypto); + if (ret) + goto out; + + ret = decode_Checksum(pa->padata_value.data, + pa->padata_value.length, + &cksum, NULL); + if (ret) { + krb5_crypto_destroy(context, crypto); + goto out; + } + + ret = krb5_verify_checksum(context, crypto, + KRB5_KU_AS_REQ, + request->data, request->length, + &cksum); + krb5_crypto_destroy(context, crypto); + free_Checksum(&cksum); + _krb5_debug(context, 5, "enc-ap-rep: %svalid", (ret == 0) ? "" : "in"); + if (ret) + goto out; + } + /* save session key */ creds->session.keyvalue.length = 0; @@ -688,10 +733,10 @@ _krb5_extract_ticket(krb5_context context, } /* compare client and save */ - ret = _krb5_principalname2krb5_principal (context, - &tmp_principal, - rep->kdc_rep.cname, - rep->kdc_rep.crealm); + ret = _krb5_principalname2krb5_principal(context, + &tmp_principal, + rep->kdc_rep.cname, + rep->kdc_rep.crealm); if (ret) goto out; diff --git a/lib/krb5/version-script.map b/lib/krb5/version-script.map index a7611890d..d3caa5c28 100644 --- a/lib/krb5/version-script.map +++ b/lib/krb5/version-script.map @@ -766,11 +766,25 @@ HEIMDAL_KRB5_2.0 { # kinit helper krb5_get_init_creds_opt_set_pkinit_user_certs; krb5_pk_enterprise_cert; + krb5_process_last_request; + krb5_init_creds_init; + krb5_init_creds_set_service; + krb5_init_creds_set_fast_ccache; + krb5_init_creds_set_keytab; + krb5_init_creds_get; + krb5_init_creds_set_password; + krb5_init_creds_store; + krb5_init_creds_free; # testing _krb5_aes_cts_encrypt; _krb5_n_fold; _krb5_expand_default_cc_name; + + # FAST + _krb5_fast_cf2; + _krb5_fast_armor_key; + local: *; }; diff --git a/tests/kdc/Makefile.am b/tests/kdc/Makefile.am index 2be4b03d4..92d6297bf 100644 --- a/tests/kdc/Makefile.am +++ b/tests/kdc/Makefile.am @@ -18,6 +18,7 @@ SCRIPT_TESTS = \ check-delegation \ check-des \ check-digest \ + check-fast \ check-kadmin \ check-hdb-mitdb \ check-kdc \ @@ -78,6 +79,11 @@ check-hdb-mitdb: check-hdb-mitdb.in Makefile krb5-hdb-mitdb.conf chmod +x check-hdb-mitdb.tmp mv check-hdb-mitdb.tmp check-hdb-mitdb +check-fast: check-fast.in Makefile + $(do_subst) < $(srcdir)/check-fast.in > check-fast.tmp + chmod +x check-fast.tmp + mv check-fast.tmp check-fast + check-kdc: check-kdc.in Makefile $(do_subst) < $(srcdir)/check-kdc.in > check-kdc.tmp chmod +x check-kdc.tmp diff --git a/tests/kdc/check-fast.in b/tests/kdc/check-fast.in new file mode 100644 index 000000000..7f5354fc1 --- /dev/null +++ b/tests/kdc/check-fast.in @@ -0,0 +1,183 @@ +#!/bin/sh +# +# Copyright (c) 2006 - 2011 Kungliga Tekniska Högskolan +# (Royal Institute of Technology, Stockholm, Sweden). +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# 3. Neither the name of the Institute nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. + +top_builddir="@top_builddir@" +env_setup="@env_setup@" +objdir="@objdir@" + +. ${env_setup} + +KRB5_CONFIG="${1-${objdir}/krb5.conf}" +export KRB5_CONFIG + +testfailed="echo test failed; cat messages.log; exit 1" + +# If there is no useful db support compile in, disable test +#${have_db} || exit 77 + +R=TEST.H5L.SE + +port=@port@ + +kadmin="${kadmin} -l -r $R" +kdc="${kdc} --addresses=localhost -P $port" + +server=host/datan.test.h5l.se +cache="FILE:${objdir}/cache.krb5" +acache="FILE:${objdir}/acache.krb5" + +kinit="${kinit} -c $cache ${afs_no_afslog}" +akinit="${kinit} -c $acache ${afs_no_afslog}" +klist="${klist} -c $cache" +aklist="${klist} -c $acache" +kgetcred="${kgetcred} -c $cache" +kdestroy="${kdestroy} -c $cache ${afs_no_unlog}" + +rm -f ${keytabfile} +rm -f current-db* +rm -f out-* +rm -f mkey.file* + +> messages.log + +echo Creating database +${kadmin} \ + init \ + --realm-max-ticket-life=1day \ + --realm-max-renewable-life=1month \ + ${R} || exit 1 + +${kadmin} add -p foo --use-defaults foo@${R} || exit 1 +${kadmin} add -p foo --use-defaults ${server}@${R} || exit 1 + +echo "Doing database check" +${kadmin} check ${R} || exit 1 + +echo foo > ${objdir}/foopassword + +echo Starting kdc +env MallocStackLogging=1 MallocStackLoggingNoCompact=1 MallocErrorAbort=1 MallocLogFile=${objdir}/malloc-log \ +${kdc} & +kdcpid=$! + +sh ${wait_kdc} +if [ "$?" != 0 ] ; then + kill -9 ${kdcpid} + exit 1 +fi + +trap "kill -9 ${kdcpid}; echo signal killing kdc; exit 1;" EXIT + +ec=0 + +# +# Check armor ticket +# + +echo "Getting client initial tickets"; > messages.log +${kinit} --password-file=${objdir}/foopassword foo@$R || \ + { ec=1 ; eval "${testfailed}"; } +echo "Checking for FAST avail" +${klist} --hidden | grep fast_avail > /dev/null || { exit 1; } +echo "Getting tickets"; > messages.log +${kgetcred} ${server}@${R} || { ec=1 ; eval "${testfailed}"; } +echo "Listing tickets"; > messages.log +${klist} > /dev/null || { ec=1 ; eval "${testfailed}"; } +${kdestroy} + +echo "Acquire host ticket to be used as an ARMOR ticket" +${akinit} --password-file=${objdir}/foopassword ${server}@${R} >/dev/null|| { exit 1; } +echo "Checking for FAST avail (in the FAST armor cache)" +${aklist} --hidden | grep fast_avail > /dev/null || { exit 1; } + +# +# Client tests +# + +echo "Getting client initial tickets with FAST armor ticket" +${kinit} --fast-armor-cache=${acache} \ + --password-file=${objdir}/foopassword foo@$R || \ + { ec=1 ; eval "${testfailed}"; } + +echo "Checking for FAST avail (in the FAST acquired cache)" +${klist} --hidden | grep fast_avail > /dev/null || { exit 1; } + +echo "Getting service ticket" +${kgetcred} ${server}@${R} || { exit 1; } +${kdestroy} + +# +# Use MIT client tools +# + +mit=/usr/local/mitkerberos/bin + +if [ -f ${mit}/kinit ] ; then + echo "Running MIT FAST tests" + + kinitpty=${objdir}/foopassword.rkpty +cat > ${kinitpty} </dev/null|| { exit 1; } + (${aklist} | grep ${server} > /dev/null ) || { exit 1; } + + echo "Checking for FAST avail" + ${aklist} --hidden | grep fast_avail > /dev/null || { exit 1; } + + echo "Using plain to get a initial ticket" + ${rkpty} ${kinitpty} ${mit}/kinit -c ${cache} foo@${R} >/dev/null|| { exit 1; } + (${klist} | grep foo > /dev/null ) || { exit 1; } + + echo "Using FAST to get a initial ticket" + ${rkpty} ${kinitpty} ${mit}/kinit -c ${cache} -T ${acache} foo@${R} >/dev/null || { exit 1; } + (${klist} | grep foo > /dev/null ) || { exit 1; } + + echo "Checking for FAST avail" + ${klist} --hidden | grep fast_avail > /dev/null || { exit 1; } + + echo "Getting service ticket" + ${mit}/kvno -c ${cache} ${server}@${R} || { exit 1; } + +fi + + +echo "killing kdc (${kdcpid})" +sh ${leaks_kill} kdc $kdcpid || exit 1 + +trap "" EXIT + +exit $ec diff --git a/tests/kdc/check-kdc.in b/tests/kdc/check-kdc.in deleted file mode 100644 index 8522c65d6..000000000 --- a/tests/kdc/check-kdc.in +++ /dev/null @@ -1,554 +0,0 @@ -#!/bin/sh -# -# Copyright (c) 2006 - 2007 Kungliga Tekniska Högskolan -# (Royal Institute of Technology, Stockholm, Sweden). -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# -# 1. Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# -# 2. Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# -# 3. Neither the name of the Institute nor the names of its contributors -# may be used to endorse or promote products derived from this software -# without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE -# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -# SUCH DAMAGE. - -top_builddir="@top_builddir@" -env_setup="@env_setup@" -objdir="@objdir@" - -. ${env_setup} - -KRB5_CONFIG="${1-${objdir}/krb5.conf}" -export KRB5_CONFIG - -testfailed="echo test failed; cat messages.log; exit 1" - -# If there is no useful db support compile in, disable test -${have_db} || exit 77 - -R=TEST.H5L.SE -R2=TEST2.H5L.SE -R3=TEST-HTTP.H5L.SE - -port=@port@ - -kadmin="${kadmin} -l -r $R" -kdc="${kdc} --addresses=localhost -P $port" - -server=host/datan.test.h5l.se -server2=host/computer.example.com -serverip=host/10.11.12.13 -serveripname=host/ip.test.h5l.org -serveripname2=host/10.11.12.14 -alias1=host/datan.example.com -alias2=host/datan -aliaskeytab=host/datan -cache="FILE:${objdir}/cache.krb5" -ocache="FILE:${objdir}/ocache.krb5" -o2cache="FILE:${objdir}/o2cache.krb5" -icache="FILE:${objdir}/icache.krb5" -keytabfile=${objdir}/server.keytab -keytab="FILE:${keytabfile}" -ps="proxy-service@${R}" -aesenctype="aes256-cts-hmac-sha1-96" - -kinit="${kinit} -c $cache ${afs_no_afslog}" -klist="${klist} -c $cache" -kgetcred="${kgetcred} -c $cache" -kgetcred_imp="${kgetcred} -c $cache --out-cache=${ocache}" -kdestroy="${kdestroy} -c $cache ${afs_no_unlog}" -kimpersonate="${kimpersonate} -k ${keytab} --ccache=${ocache}" - -rm -f ${keytabfile} -rm -f current-db* -rm -f out-* -rm -f mkey.file* - -> messages.log - -echo Creating database -${kadmin} \ - init \ - --realm-max-ticket-life=1day \ - --realm-max-renewable-life=1month \ - ${R} || exit 1 - -${kadmin} \ - init \ - --realm-max-ticket-life=1day \ - --realm-max-renewable-life=1month \ - ${R2} || exit 1 - -${kadmin} \ - init \ - --realm-max-ticket-life=1day \ - --realm-max-renewable-life=1month \ - ${R3} || exit 1 - -${kadmin} cpw -r krbtgt/${R}@${R} || exit 1 -${kadmin} cpw -r krbtgt/${R}@${R} || exit 1 -${kadmin} cpw -r krbtgt/${R}@${R} || exit 1 -${kadmin} cpw -r krbtgt/${R}@${R} || exit 1 - -${kadmin} add -p foo --use-defaults foo@${R} || exit 1 -${kadmin} add -p bar --use-defaults bar@${R} || exit 1 -${kadmin} add -p foo --use-defaults remove@${R} || exit 1 -${kadmin} add -p kaka --use-defaults ${server}@${R} || exit 1 -${kadmin} add -p kaka --use-defaults ${server}-des3@${R} || exit 1 -${kadmin} add -p kaka --use-defaults kt-des3@${R} || exit 1 -${kadmin} add -p foo --use-defaults ${ps} || exit 1 -${kadmin} modify --attributes=+trusted-for-delegation ${ps} || exit 1 -${kadmin} modify --constrained-delegation=${server} ${ps} || exit 1 -${kadmin} ext -k ${keytab} ${server}@${R} || exit 1 -${kadmin} ext -k ${keytab} ${ps} || exit 1 - -${kadmin} add -p kaka --use-defaults ${server2}@${R2} || exit 1 -${kadmin} ext -k ${keytab} ${server2}@${R2} || exit 1 -${kadmin} add -p kaka --use-defaults ${serverip}@${R} || exit 1 -${kadmin} ext -k ${keytab} ${serverip}@${R} || exit 1 -${kadmin} add -p kaka --use-defaults ${serveripname}@${R} || exit 1 -${kadmin} ext -k ${keytab} ${serveripname}@${R} || exit 1 -${kadmin} modify --alias=${serveripname2}@${R} ${serveripname}@${R} -${kadmin} add -p foo --use-defaults remove2@${R2} || exit 1 - -${kadmin} add -p kaka --use-defaults ${alias1}@${R} || exit 1 -${kadmin} ext -k ${keytab} ${alias1}@${R} || exit 1 -${kadmin} modify --alias=${alias2}@${R} ${alias1}@${R} - -${kadmin} add -p cross1 --use-defaults krbtgt/${R2}@${R} || exit 1 -${kadmin} add -p cross2 --use-defaults krbtgt/${R}@${R2} || exit 1 - -${kadmin} add -p foo --use-defaults pw-expire@${R} || exit 1 -${kadmin} modify --pw-expiration-time=+1day pw-expire@${R} || exit 1 - -${kadmin} add -p foo --use-defaults foo@${R3} || exit 1 - -echo "Check parser" -${kadmin} add -p foo --use-defaults -- -p || exit 1 -${kadmin} delete -- -p || exit 1 - -echo "Doing database check" -${kadmin} check ${R} || exit 1 -${kadmin} check ${R2} || exit 1 - -echo "Extracting enctypes" -${ktutil} -k ${keytab} list > tempfile || exit 1 -${EGREP} -v '^FILE:' tempfile | ${EGREP} -v '^Vno' | ${EGREP} -v '^$' | \ - awk '$1 !~ /1/ { exit 1 }' || exit 1 - -${kadmin} get foo@${R} > tempfile || exit 1 -enctypes=`grep Keytypes: tempfile | sed 's/(pw-salt)//g' | sed 's/,//g' | sed 's/Keytypes://' | sed 's/\[[0-9]*\]//g'` - -enctype_sans_aes=`echo $enctypes | sed 's/aes[^ ]*//g'` -enctype_sans_des3=`echo $enctypes | sed 's/des3-cbc-sha1//g'` - -echo "deleting all but des enctypes on kt-des3 in keytab" -${kadmin} ext -k ${keytab} kt-des3@${R} || exit 1 -for a in ${enctype_sans_des3} ; do - ${ktutil} -k ${keytab} remove -p kt-des3@${R} -e $a -done - -echo foo > ${objdir}/foopassword - -echo Starting kdc -env MallocStackLogging=1 MallocStackLoggingNoCompact=1 MallocErrorAbort=1 MallocLogFile=${objdir}/malloc-log \ -${kdc} & -kdcpid=$! - -sh ${wait_kdc} -if [ "$?" != 0 ] ; then - kill -9 ${kdcpid} - exit 1 -fi - -trap "kill -9 ${kdcpid}; echo signal killing kdc; exit 1;" EXIT - -ec=0 - -echo "Getting client initial tickets"; > messages.log -${kinit} --password-file=${objdir}/foopassword foo@$R || \ - { ec=1 ; eval "${testfailed}"; } -echo "Doing krbtgt key rollover"; > messages.log -${kadmin} cpw -r --keepold krbtgt/${R}@${R} || exit 1 -echo "Getting tickets"; > messages.log -${kgetcred} ${server}@${R} || { ec=1 ; eval "${testfailed}"; } -echo "Listing tickets"; > messages.log -${klist} > /dev/null || { ec=1 ; eval "${testfailed}"; } -${test_ap_req} ${server}@${R} ${keytab} ${cache} || \ - { ec=1 ; eval "${testfailed}"; } -${kdestroy} - -echo "Getting client initial tickets (http transport)"; > messages.log -${kinit} --password-file=${objdir}/foopassword foo@${R3} || \ - { ec=1 ; eval "${testfailed}"; } -${kdestroy} - -echo "Specific enctype"; > messages.log -${kinit} --password-file=${objdir}/foopassword \ - -e ${aesenctype} -e ${aesenctype} \ - foo@$R || \ - { ec=1 ; eval "${testfailed}"; } - -for a in $enctypes; do - echo "Getting client initial tickets ($a)"; > messages.log - ${kinit} --enctype=$a --password-file=${objdir}/foopassword foo@$R || { ec=1 ; eval "${testfailed}"; } - echo "Getting tickets"; > messages.log - ${kgetcred} ${server}@${R} || { ec=1 ; eval "${testfailed}"; } - ${test_ap_req} ${server}@${R} ${keytab} ${cache} || { ec=1 ; eval "${testfailed}"; } - ${kdestroy} -done - - -echo "Getting client initial tickets"; > messages.log -${kinit} --password-file=${objdir}/foopassword foo@$R || \ - { ec=1 ; eval "${testfailed}"; } -for a in $enctypes; do - echo "Getting tickets ($a)"; > messages.log - ${kgetcred} -e $a ${server}@${R} || { ec=1 ; eval "${testfailed}"; } - ${test_ap_req} ${server}@${R} ${keytab} ${cache} || \ - { ec=1 ; eval "${testfailed}"; } - ${kdestroy} --credential=${server}@${R} -done -${kdestroy} - -echo "Getting client initial tickets for cross realm case"; > messages.log -${kinit} --password-file=${objdir}/foopassword foo@$R || { ec=1 ; eval "${testfailed}"; } -for a in $enctypes; do - echo "Getting cross realm tickets ($a)"; > messages.log - ${kgetcred} -e $a ${server2}@${R2} || { ec=1 ; eval "${testfailed}"; } - echo " checking we we got back right ticket" - ${klist} | grep ${server2}@ > /dev/null || { ec=1 ; eval "${testfailed}"; } - echo " checking if ticket is useful" - ${test_ap_req} ${server2}@${R2} ${keytab} ${cache} || \ - { ec=1 ; eval "${testfailed}"; } - ${kdestroy} --credential=${server2}@${R2} -done -${kdestroy} - -echo "try all permutations"; > messages.log -for a in $enctypes; do - echo "Getting client initial tickets ($a)"; > messages.log - ${kinit} --enctype=$a --password-file=${objdir}/foopassword foo@$R || \ - { ec=1 ; eval "${testfailed}"; } - for b in $enctypes; do - echo "Getting tickets ($a -> $b)"; > messages.log - ${kgetcred} -e $b ${server}@${R} || \ - { ec=1 ; eval "${testfailed}"; } - ${test_ap_req} ${server}@${R} ${keytab} ${cache} || \ - { ec=1 ; eval "${testfailed}"; } - ${kdestroy} --credential=${server}@${R} - done - ${kdestroy} -done - -echo "Getting client initial tickets ip based name"; > messages.log -${kinit} --password-file=${objdir}/foopassword foo@$R || { ec=1 ; eval "${testfailed}"; } -echo "Getting ip based name tickets"; > messages.log -${kgetcred} ${serverip}@${R} || { ec=1 ; eval "${testfailed}"; } -echo " checking we we got back right ticket" -${klist} | grep ${serverip}@ > /dev/null || { ec=1 ; eval "${testfailed}"; } -echo " checking if ticket is useful" -${test_ap_req} ${serverip}@${R} ${keytab} ${cache} || \ - { ec=1 ; eval "${testfailed}"; } -${kdestroy} - -echo "Getting client initial tickets ip based name (alias)"; > messages.log -${kinit} --password-file=${objdir}/foopassword foo@$R || { ec=1 ; eval "${testfailed}"; } -for a in ${serveripname} ${serveripname2} ; do - echo "Getting ip based name tickets (alias) $a"; > messages.log - ${kgetcred} ${a}@${R} || { ec=1 ; eval "${testfailed}"; } - echo " checking we we got back right ticket" - ${klist} | grep ${a}@ > /dev/null || { ec=1 ; eval "${testfailed}"; } - echo " checking if ticket is useful" - ${test_ap_req} --server-any ${a}@${R} ${keytab} ${cache} || \ - { ec=1 ; eval "${testfailed}"; } -done -${kdestroy} - -echo "Getting server initial tickets"; > messages.log -${kinit} --keytab=${keytab} ${server}@$R || { ec=1 ; eval "${testfailed}"; } -echo "Listing tickets"; > messages.log -${klist} | grep "Principal: ${server}" > /dev/null || \ - { ec=1 ; eval "${testfailed}"; } -${kdestroy} - -echo "Getting key for key that are a subset in keytab compared to kdb" -${kinit} --keytab=${keytab} kt-des3@${R} -${klist} | grep "Principal: kt-des3" > /dev/null || \ - { ec=1 ; eval "${testfailed}"; } -${kdestroy} - -echo "initial tickets for deleted user test case"; > messages.log -${kinit} --password-file=${objdir}/foopassword remove@$R || \ - { ec=1 ; eval "${testfailed}"; } -${kadmin} delete remove@${R} || { ec=1 ; eval "${testfailed}"; } -echo "try getting ticket with deleted user"; > messages.log -${kgetcred} ${server}@${R} 2> /dev/null && { ec=1 ; eval "${testfailed}"; } -${kdestroy} - -echo "cross realm case (deleted user)"; > messages.log -${kinit} --password-file=${objdir}/foopassword remove2@$R2 || \ - { ec=1 ; eval "${testfailed}"; } -${kgetcred} krbtgt/${R}@${R2} 2> /dev/null || \ - { ec=1 ; eval "${testfailed}"; } -${kadmin} delete remove2@${R2} || exit 1 -${kgetcred} ${server}@${R} 2> /dev/null || \ - { ec=1 ; eval "${testfailed}"; } -${kdestroy} - -echo "rename user"; > messages.log -${kadmin} add -p foo --use-defaults rename@${R} || exit 1 -${kinit} --password-file=${objdir}/foopassword rename@${R} || \ - { ec=1 ; eval "${testfailed}"; } -${kadmin} rename rename@${R} rename2@${R} || exit 1 -${kinit} --password-file=${objdir}/foopassword rename2@${R} || \ - { ec=1 ; eval "${testfailed}"; } -${kdestroy} -${kadmin} delete rename2@${R} || exit 1 - -echo "rename user to another realm"; > messages.log -${kadmin} add -p foo --use-defaults rename@${R} || exit 1 -${kinit} --password-file=${objdir}/foopassword rename@${R} || \ - { ec=1 ; eval "${testfailed}"; } -${kadmin} rename rename@${R} rename@${R2} || exit 1 -${kinit} --password-file=${objdir}/foopassword rename@${R2} || \ - { ec=1 ; eval "${testfailed}"; } -${kdestroy} -${kadmin} delete rename@${R2} || exit 1 - -echo deleting all but aes enctypes on krbtgt -${kadmin} del_enctype krbtgt/${R}@${R} ${enctype_sans_aes} || exit 1 - -echo deleting all but des enctypes on server-des3 -${kadmin} del_enctype ${server}-des3@${R} ${enctype_sans_des3} || exit 1 -${kadmin} ext -k ${keytab} ${server}-des3@${R} || exit 1 - -echo "try all permutations (only aes)"; > messages.log -for a in $enctypes; do - echo "Getting client initial tickets ($a)"; > messages.log - ${kinit} --enctype=$a --password-file=${objdir}/foopassword foo@${R} ||\ - { ec=1 ; eval "${testfailed}"; } - for b in $enctypes; do - echo "Getting tickets ($a -> $b)"; > messages.log - ${kgetcred} -e $b ${server}@${R} || \ - { ec=1 ; eval "${testfailed}"; } - ${test_ap_req} ${server}@${R} ${keytab} ${cache} || \ - { ec=1 ; eval "${testfailed}"; } - - echo "Getting tickets ($a -> $b) (server des3 only)"; > messages.log - ${kgetcred} ${server}-des3@${R} || \ - { ec=1 ; eval "${testfailed}"; } - ${test_ap_req} ${server}-des3@${R} ${keytab} ${cache} || \ - { ec=1 ; eval "${testfailed}"; } - - ${kdestroy} --credential=${server}@${R} - ${kdestroy} --credential=${server}-des3@${R} - done - ${kdestroy} -done - -echo deleting all enctypes on krbtgt -${kadmin} del_enctype krbtgt/${R}@${R} aes256-cts-hmac-sha1-96 || \ - { ec=1 ; eval "${testfailed}"; } -echo "try initial ticket w/o and keys on krbtgt" -${kinit} --password-file=${objdir}/foopassword foo@${R} 2>/dev/null && \ - { ec=1 ; eval "${testfailed}"; } -echo "adding random aes key" -${kadmin} add_enctype -r krbtgt/${R}@${R} aes256-cts-hmac-sha1-96 || \ - { ec=1 ; eval "${testfailed}"; } -echo "try initial ticket with random aes key on krbtgt" -${kinit} --password-file=${objdir}/foopassword foo@${R} || \ - { ec=1 ; eval "${testfailed}"; } -${kdestroy} - -rsa=yes -ecdsa=yes -pkinit=no -if ${hxtool} info | grep 'rsa: hx509 null RSA' > /dev/null ; then - rsa=no -fi -if ${hxtool} info | grep 'rand: not available' > /dev/null ; then - rsa=no -fi -if ${kinit} --help 2>&1 | grep "CA certificates" > /dev/null; then - pkinit=yes -fi - -if ${hxtool} info | grep 'ecdsa: hcrypto null' > /dev/null ; then - ecdsa=no -fi - - -# If we support pkinit and have RSA, lets try that -if test "$pkinit" = yes -a "$rsa" = yes ; then - - echo "try anonymous pkinit"; > messages.log - ${kinit} --anonymous ${R} || \ - { ec=1 ; eval "${testfailed}"; } - ${kgetcred} ${server}@${R} || { ec=1 ; eval "${testfailed}"; } - ${kdestroy} - - for type in "" "--pk-use-enckey"; do - echo "Trying pk-init (principal in certificate) $type"; > messages.log - ${kinit} $type -C FILE:${hx509_data}/pkinit.crt,${hx509_data}/pkinit.key bar@${R} || \ - { ec=1 ; eval "${testfailed}"; } - ${kgetcred} ${server}@${R} || { ec=1 ; eval "${testfailed}"; } - ${kdestroy} - - echo "Trying pk-init (principal in pki-mapping) $type"; > messages.log - ${kinit} $type -C FILE:${hx509_data}/pkinit.crt,${hx509_data}/pkinit.key foo@${R} || \ - { ec=1 ; eval "${testfailed}"; } - ${kgetcred} ${server}@${R} || { ec=1 ; eval "${testfailed}"; } - ${kdestroy} - - echo "Trying pk-init (password protected key) $type"; > messages.log - ${kinit} $type -C FILE:${hx509_data}/pkinit.crt,${hx509_data}/pkinit-pw.key --password-file=${objdir}/foopassword foo@${R} || \ - { ec=1 ; eval "${testfailed}"; } - ${kgetcred} ${server}@${R} || \ - { ec=1 ; eval "${testfailed}"; } - ${kdestroy} - - echo "Trying pk-init (proxy cert) $type"; > messages.log - ${kinit} $type -C FILE:${hx509_data}/pkinit-proxy-chain.crt,${hx509_data}/pkinit-proxy.key foo@${R} || \ - { ec=1 ; eval "${testfailed}"; } - ${kgetcred} ${server}@${R} || { ec=1 ; eval "${testfailed}"; } - ${kdestroy} - - done - - if test "$ecdsa" = yes > /dev/null ; then - echo "Trying pk-init (ec certificate)" - > messages.log - ${kinit} -C FILE:${hx509_data}/pkinit-ec.crt,${hx509_data}/pkinit-ec.key bar@${R} || \ - { ec=1 ; eval "${testfailed}"; } - ${kgetcred} ${server}@${R} || { ec=1 ; eval "${testfailed}"; } - ${kdestroy} - grep 'PK-INIT using ecdh' messages.log > /dev/null || \ - { ec=1 ; eval "${testfailed}"; } - fi - -else - echo "no pkinit (pkinit: $pkinit, rsa: $rsa)"; > messages.log -fi - -echo "tickets for impersonate test case"; > messages.log -${kinit} --forwardable --password-file=${objdir}/foopassword ${ps} || \ - { ec=1 ; eval "${testfailed}"; } -${kgetcred_imp} --impersonate=bar@${R} ${ps} || \ - { ec=1 ; eval "${testfailed}"; } -${test_ap_req} ${ps} ${keytab} ${ocache} || \ - { ec=1 ; eval "${testfailed}"; } -echo " negative check" -${kgetcred_imp} --impersonate=bar@${R} foo@${R} 2>/dev/null && \ - { ec=1 ; eval "${testfailed}"; } - -echo "test constrained delegation"; > messages.log -${kgetcred_imp} --forward --impersonate=bar@${R} ${ps} || \ - { ec=1 ; eval "${testfailed}"; } -${kgetcred} \ - --out-cache=${o2cache} \ - --delegation-credential-cache=${ocache} \ - ${server}@${R} || \ - { ec=1 ; eval "${testfailed}"; } -echo " try using the credential" -${test_ap_req} ${server}@${R} ${keytab} ${o2cache} || \ - { ec=1 ; eval "${testfailed}"; } -echo " negative check" -${kgetcred} \ - --out-cache=${o2cache} \ - --delegation-credential-cache=${ocache} \ - bar@${R} 2>/dev/null && \ - { ec=1 ; eval "${testfailed}"; } - -echo "test constrained delegation impersonation (non forward)"; > messages.log -rm -f ocache.krb5 -${kimpersonate} -s ${ps} -c bar@${R} -t ${aesenctype} || \ - { ec=1 ; eval "${testfailed}"; } -${kgetcred} --out-cache=${o2cache} --delegation-credential-cache=${ocache} ${server}@${R} > /dev/null 2>/dev/null && \ - { ec=1 ; eval "${testfailed}"; } - -echo "test constrained delegation impersonation (missing KRB5SignedPath)"; > messages.log -rm -f ocache.krb5 -${kimpersonate} -s ${ps} -c bar@${R} -t ${aesenctype} -f forwardable || \ - { ec=1 ; eval "${testfailed}"; } -${kgetcred} --out-cache=${o2cache} --delegation-credential-cache=${ocache} ${server}@${R} > /dev/null 2>/dev/null && \ - { ec=1 ; eval "${testfailed}"; } - -${kdestroy} - -echo "check renewing" > messages.log -${kinit} --renewable --password-file=${objdir}/foopassword foo@$R || \ - { ec=1 ; eval "${testfailed}"; } -echo "kinit -R" -${kinit} -R || \ - { ec=1 ; eval "${testfailed}"; } -echo "check renewing MIT interface" > messages.log -${kinit} --renewable --password-file=${objdir}/foopassword foo@$R || \ - { ec=1 ; eval "${testfailed}"; } -echo "test_renew" -env KRB5CCNAME=${cache} ${test_renew} || \ - { ec=1 ; eval "${testfailed}"; } -${kdestroy} - -echo "checking server aliases"; > messages.log -${kinit} --password-file=${objdir}/foopassword foo@$R || \ - { ec=1 ; eval "${testfailed}"; } -echo "Getting tickets"; > messages.log -${kgetcred} ${alias1}@${R} || { ec=1 ; eval "${testfailed}"; } -${kgetcred} ${alias2}@${R} || { ec=1 ; eval "${testfailed}"; } -echo " verify entry in keytab" -${test_ap_req} ${alias1}@${R} ${keytab} ${cache} || \ - { ec=1 ; eval "${testfailed}"; } -echo " verify entry in keytab with any" -${test_ap_req} --server-any ${alias1}@${R} ${keytab} ${cache} || \ - { ec=1 ; eval "${testfailed}"; } -echo " verify failure with alias entry" -${test_ap_req} ${alias2}@${R} ${keytab} ${cache} 2>/dev/null && \ - { ec=1 ; eval "${testfailed}"; } -echo " verify alias entry in keytab with any" -${test_ap_req} --server-any ${alias2}@${R} ${keytab} ${cache} || \ - { ec=1 ; eval "${testfailed}"; } -${kdestroy} - -echo "testing removal of keytab" -${ktutil} -k ${keytab} destroy || { ec=1 ; eval "${testfailed}"; } -test -f ${keytabfile} && { ec=1 ; eval "${testfailed}"; } - -echo "Getting client pw expire"; > messages.log -${kinit} --password-file=${objdir}/foopassword \ - pw-expire@${R} 2>kinit-log.tmp|| \ - { ec=1 ; eval "${testfailed}"; } -grep 'Your password will expire' kinit-log.tmp > /dev/null || \ - { ec=1 ; eval "${testfailed}"; } -echo " kinit passes" -${test_gic} --client=pw-expire@${R} --password=foo > kinit-log.tmp 2>/dev/null -${EGREP} "^e type: 6" kinit-log.tmp > /dev/null || \ - { ec=1 ; eval "${testfailed}"; } -echo " test_gic passes" -${kdestroy} - -echo "killing kdc (${kdcpid})" -sh ${leaks_kill} kdc $kdcpid || exit 1 - -trap "" EXIT - -exit $ec diff --git a/tests/plugin/windc.c b/tests/plugin/windc.c index 0956ce831..44518dcf7 100644 --- a/tests/plugin/windc.c +++ b/tests/plugin/windc.c @@ -69,7 +69,7 @@ client_access(void *ctx, hdb_entry_ex *client, const char *client_name, hdb_entry_ex *server, const char *server_name, KDC_REQ *req, - krb5_data *e_data) + METHOD_DATA *data) { krb5_warnx(context, "client_access"); return 0;