kdc: support for GSS-API pre-authentication
Add support for GSS-API pre-authentication to the KDC, using a simplified variation of draft-perez-krb-wg-gss-preauth-02 that encodes GSS-API context tokens directly in PADATA, and uses FX-COOKIE for state management. More information on the protocol and implementation may be found in lib/gssapi/preauth/README.md.
This commit is contained in:
616
doc/standardisation/draft-perez-krb-wg-gss-preauth-02.txt
Normal file
616
doc/standardisation/draft-perez-krb-wg-gss-preauth-02.txt
Normal file
@@ -0,0 +1,616 @@
|
||||
|
||||
|
||||
|
||||
Kerberos Working Group A. Perez-Mendez
|
||||
Internet-Draft R. Marin-Lopez
|
||||
Intended status: Experimental F. Pereniguez-Garcia
|
||||
Expires: March 5, 2013 G. Lopez-Millan
|
||||
University of Murcia
|
||||
Sep 2012
|
||||
|
||||
|
||||
GSS-API pre-authentication for Kerberos
|
||||
draft-perez-krb-wg-gss-preauth-02
|
||||
|
||||
Abstract
|
||||
|
||||
This document describes a pre-authentication mechanism for Kerberos
|
||||
based on the Generic Security Service Application Program Interface
|
||||
(GSS-API), which allows a Key Distribution Center (KDC) to
|
||||
authenticate clients by using a GSS mechanism.
|
||||
|
||||
Status of this Memo
|
||||
|
||||
This Internet-Draft is submitted in full conformance with the
|
||||
provisions of BCP 78 and BCP 79.
|
||||
|
||||
Internet-Drafts are working documents of the Internet Engineering
|
||||
Task Force (IETF). Note that other groups may also distribute
|
||||
working documents as Internet-Drafts. The list of current Internet-
|
||||
Drafts is at http://datatracker.ietf.org/drafts/current/.
|
||||
|
||||
Internet-Drafts are draft documents valid for a maximum of six months
|
||||
and may be updated, replaced, or obsoleted by other documents at any
|
||||
time. It is inappropriate to use Internet-Drafts as reference
|
||||
material or to cite them other than as "work in progress."
|
||||
|
||||
This Internet-Draft will expire on March 5, 2013.
|
||||
|
||||
Copyright Notice
|
||||
|
||||
Copyright (c) 2012 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
|
||||
|
||||
|
||||
|
||||
Perez-Mendez, et al. Expires March 5, 2013 [Page 1]
|
||||
|
||||
Internet-Draft GSS preauth Sep 2012
|
||||
|
||||
|
||||
described in the Simplified BSD License.
|
||||
|
||||
|
||||
Table of Contents
|
||||
|
||||
1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . . 3
|
||||
1.1. Requirements Language . . . . . . . . . . . . . . . . . . 4
|
||||
2. Motivation . . . . . . . . . . . . . . . . . . . . . . . . . . 4
|
||||
3. Definition of the Kerberos GSS padata . . . . . . . . . . . . 4
|
||||
4. GSS Pre-authentication Operation . . . . . . . . . . . . . . . 5
|
||||
4.1. Generation of GSS preauth requests . . . . . . . . . . . . 5
|
||||
4.2. Processing of GSS preauth requests . . . . . . . . . . . . 6
|
||||
4.3. Generation of GSS preauth responses . . . . . . . . . . . 6
|
||||
4.4. Processing of GSS preauth responses . . . . . . . . . . . 7
|
||||
5. Data in the KDC_ERR_PREAUTH_REQUIRED . . . . . . . . . . . . . 7
|
||||
6. Derivation of the reply key from the GSS context . . . . . . . 7
|
||||
7. KDC state management . . . . . . . . . . . . . . . . . . . . . 8
|
||||
8. Support for federated users . . . . . . . . . . . . . . . . . 8
|
||||
9. GSS channel bindings . . . . . . . . . . . . . . . . . . . . . 9
|
||||
10. Acknowledgements . . . . . . . . . . . . . . . . . . . . . . . 9
|
||||
11. Security Considerations . . . . . . . . . . . . . . . . . . . 9
|
||||
12. IANA Considerations . . . . . . . . . . . . . . . . . . . . . 9
|
||||
13. Normative References . . . . . . . . . . . . . . . . . . . . . 9
|
||||
Authors' Addresses . . . . . . . . . . . . . . . . . . . . . . . . 10
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Perez-Mendez, et al. Expires March 5, 2013 [Page 2]
|
||||
|
||||
Internet-Draft GSS preauth Sep 2012
|
||||
|
||||
|
||||
1. Introduction
|
||||
|
||||
The GSS-API (Generic Security Service Application Programming
|
||||
Interface) [RFC2743] provides a generic toolset of functions that
|
||||
allow applications to establish security contexts in order to protect
|
||||
their communications through security services such as
|
||||
authentication, confidentiality and integrity protection. Thanks to
|
||||
the GSS-API, applications remain independent from the specific
|
||||
underlying mechanism used to establish the context and provide
|
||||
security.
|
||||
|
||||
On the other hand, Kerberos [RFC4120] defines a process called pre-
|
||||
authentication. This feature is intended to avoid the security risk
|
||||
of providing tickets encrypted with the user's long-term key to
|
||||
attackers, by requiring clients to proof their knowledge over these
|
||||
credentials. The execution of a pre-authentication mechanism may
|
||||
require the exchange of several KRB_AS_REQ/KRB_ERROR messages before
|
||||
the KDC delivers the TGT requested by the client within a KRB_AS_REP.
|
||||
These messages transport authentication information by means of pre-
|
||||
authentication elements.
|
||||
|
||||
There exists a variety of pre-authentication mechanisms, like PKINIT
|
||||
[RFC4556] and encrypted time-stamp [RFC4120]. Furthermore,
|
||||
[I-D.ietf-krb-wg-preauth-framework] provides a generic framework for
|
||||
Kerberos pre-authentication, which aims to describe the features that
|
||||
a pre-authentication mechanism may provide (e.g. mutual
|
||||
authentication, replace reply key, etc.). Additionally, in order to
|
||||
simplify the definition of new pre-authentication mechanisms, it
|
||||
defines a mechanism called FAST (Flexible Authentication Secure
|
||||
Tunneling), which provides a generic and secure transport for pre-
|
||||
authentication elements. More specifically, FAST establishes a
|
||||
secure tunnel providing confidentiality and integrity protection
|
||||
between the client and the KDC prior to the exchange of any specific
|
||||
pre-authentication data. Within this tunnel, different pre-
|
||||
authentication methods can be executed. This inner mechanism is
|
||||
called a FAST factor. It is important to note that FAST factors
|
||||
cannot usually be used outside the FAST pre-authentication method
|
||||
since they assume the underlying security layer provided by FAST.
|
||||
|
||||
The aim of this draft is to define a new pre-authentication
|
||||
mechanism, following the recommendations of
|
||||
[I-D.ietf-krb-wg-preauth-framework], that relies on the GSS-API
|
||||
security services to pre-authenticate clients. This pre-
|
||||
authentication mechanism will allow the KDC to authenticate clients
|
||||
making use of any current or future GSS mechanism, as long as they
|
||||
satisfy the minimum security requirements described in this
|
||||
specification. The Kerberos client will play the role of the GSS
|
||||
initiator, while the Authentication Server (AS) in the KDC will play
|
||||
|
||||
|
||||
|
||||
Perez-Mendez, et al. Expires March 5, 2013 [Page 3]
|
||||
|
||||
Internet-Draft GSS preauth Sep 2012
|
||||
|
||||
|
||||
the role of the GSS acceptor.
|
||||
|
||||
1.1. Requirements Language
|
||||
|
||||
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 RFC 2119 [RFC2119].
|
||||
|
||||
|
||||
2. Motivation
|
||||
|
||||
This work is mainly motivated by the necessity of a way to allow the
|
||||
KDC to make use of the technologies defined in the ABFAB WG to
|
||||
perform the access control of federated users. Specifically, the
|
||||
ABFAB architecture requires relying parties to make use of the GSS-
|
||||
EAP mechanism to perform authentication.
|
||||
[I-D.perez-abfab-eap-gss-preauth] defines how GSS-EAP is transported
|
||||
on top of the GSS pre-authentication mechanism defined in this
|
||||
document.
|
||||
|
||||
|
||||
3. Definition of the Kerberos GSS padata
|
||||
|
||||
To establish the security context, the GSS-API defines the exchange
|
||||
of GSS tokens between the initiator and the acceptor. These tokens,
|
||||
which contain mechanism-specific information, are completely opaque
|
||||
to the application. However, how these tokens are transported
|
||||
between the initiator and the responder depends on the specific
|
||||
application. Since GSS-API is defined as independent of the
|
||||
underlying communications service, its use does not require to
|
||||
implement any specific security feature for the transport. For
|
||||
instance, tokens could just be sent by means of plain UDP datagrams.
|
||||
For this reason, security and ordered delivery of information must be
|
||||
implemented by each specific GSS mechanism (if required).
|
||||
|
||||
Therefore, GSS tokens are the atomic piece of information from the
|
||||
application point of view when using GSS-API, which require a proper
|
||||
transport between the initiator (Kerberos client) and the acceptor
|
||||
(AS). In particular, the proposed GSS-based pre-authentication
|
||||
mechanism defines a new pre-authentication element (hereafter padata)
|
||||
called PA-GSS, to transport a generic GSS token from the Kerberos
|
||||
client to the AS and vice-versa. This padata also transport state
|
||||
information required to maintain the KDC stateless (see section
|
||||
Section 7.
|
||||
|
||||
PA-GSS To be defined (TBD)
|
||||
|
||||
A PA-GSS padata element contains the ASN.1 DER encoding of the PA-GSS
|
||||
|
||||
|
||||
|
||||
Perez-Mendez, et al. Expires March 5, 2013 [Page 4]
|
||||
|
||||
Internet-Draft GSS preauth Sep 2012
|
||||
|
||||
|
||||
structure:
|
||||
|
||||
PA-GSS ::= SEQUENCE {
|
||||
sec-ctx-token [0] OCTET STRING,
|
||||
state [1] EncryptedData OPTIONAL -- contains PA-GSS-STATE
|
||||
}
|
||||
|
||||
PA-GSS-STATE ::= SEQUENCE {
|
||||
timestamp [0] KerberosTime,
|
||||
exported-sec-ctx-token [1] OCTET STRING,
|
||||
...
|
||||
}
|
||||
|
||||
The sec-ctx-token element of the PA-GSS structure contains the
|
||||
output_token token returned by either, the GSS_Init_sec_context and
|
||||
the GSS_Accept_sec_context calls. The state element of the PA-GSS
|
||||
structure is optional, and will be absent in the first AS_REQ message
|
||||
from the client to the KDC and in the last AS_REP message from the
|
||||
KDC to the client. It contains a PA-GSS-STATE structure encrypted
|
||||
with the first krbtgt key and a key usage in the 512-1023 range (to
|
||||
be defined TBD). The state element is generated by the KDC, while
|
||||
the client just copy it from the previously received PA-GSS structure
|
||||
(if present).
|
||||
|
||||
The PA-GSS-STATE contains a timestamp element, meant to detect
|
||||
possible replay situations, and a exported-sec-ctx-token element,
|
||||
representing the whole GSS security context state corresponding to
|
||||
the current authentication process. This value is generated by the
|
||||
KDC by calling to the GSS_Export_sec_context, when the
|
||||
GSS_Accept_sec_context returns GSS_S_CONTINUE_NEEDED.
|
||||
|
||||
|
||||
4. GSS Pre-authentication Operation
|
||||
|
||||
4.1. Generation of GSS preauth requests
|
||||
|
||||
The Kerberos client (initiator) starts by calling to the
|
||||
GSS_Init_sec_context function. In the first call to this function,
|
||||
the client provides GSS_C_NO_CTX as the value of the context_handle
|
||||
and NULL as the input_token, given that no context has been initiated
|
||||
yet. When using multi round-trip GSS mechanisms, in subsequent calls
|
||||
to this routine the client will use both, the context_handle value
|
||||
obtained after the first call, and the input_token received from the
|
||||
KDC. The mutual_req_flag, replay_det_req_flag and sequence_req_flag
|
||||
MUST be set, as the GSS token is meant to be tranported over
|
||||
cleartext channels.
|
||||
|
||||
The GSS_Init_sec_context returns a context_handle, an output_token
|
||||
|
||||
|
||||
|
||||
Perez-Mendez, et al. Expires March 5, 2013 [Page 5]
|
||||
|
||||
Internet-Draft GSS preauth Sep 2012
|
||||
|
||||
|
||||
and a status value. In this first call, the only acceptable status
|
||||
value is GSS_S_CONTINUE_NEEDED, as the KDC is expected to provide a
|
||||
token in order to continue with the context establishment process.
|
||||
The Kerberos client creates a new PA-GSS padata, with the obtained
|
||||
output_token as the sec-ctx-token element, and with the state element
|
||||
absent. The PA-GSS padata is sent to the KDC through a KRB_AS_REQ
|
||||
message.
|
||||
|
||||
4.2. Processing of GSS preauth requests
|
||||
|
||||
When the KDC (GSS acceptor) receives a KRB_AS_REQ message containing
|
||||
a PA-GSS padata, but a state element (see Section 7) is not included,
|
||||
the KDC assumes that this is the first message of a context
|
||||
establishment, and thus GSS_C_NO_CTX is used as context_handle to
|
||||
invoke the GSS_Accept_sec_context routine. Conversely, if a state
|
||||
element is included, the KDC assumes that this message is part an
|
||||
ongoing authentication and the value of the state element is
|
||||
decrypted and used to recover the state of the authentication (see
|
||||
Section 7). In both cases, after receiving the message, the KDC
|
||||
calls to the GSS_Accept_sec_context function, using the adequate
|
||||
context_handle value and using the received token in the PA-GSS
|
||||
padata as input_token.
|
||||
|
||||
Once the execution of the GSS_Accept_sec_context function is
|
||||
completed, the KDC obtains a context_handle, an output_token that
|
||||
MUST be sent to the initiator in order to continue with the
|
||||
authentication process, and a status value. If the obtained status
|
||||
is GSS_S_COMPLETE, the client is considered authenticated. If the
|
||||
status is GSS_S_CONTINUE_NEEDED, further information is required to
|
||||
complete the process.
|
||||
|
||||
4.3. Generation of GSS preauth responses
|
||||
|
||||
Once the KDC has processed the input_token provided by the client (as
|
||||
described in Section 4.2), two main different situations may occur
|
||||
depending on the status value. If the client is successfully
|
||||
authenticated (GSS_S_COMPLETE), the KDC will reply to the client with
|
||||
a KRB_AS_REP message. This message will transport the final
|
||||
output_token in a PA-GSS padata type. This PA-GSS padata will not
|
||||
contain the state element. The reply key used to encrypt the enc-
|
||||
part field of the KRB_AS_REP message is derived from the GSS security
|
||||
context cryptographic material. Section 6 provides further details
|
||||
regarding this derivation. At this moment, the KDC also verifies
|
||||
that the cname provided in the AS_REQ matches the src_name obtained
|
||||
through the final GSS_Accept_sec_ctx call (except when WELLKNOWN/
|
||||
FEDERATED is used as cname Section 8).
|
||||
|
||||
On the contrary, if further data is required to complete the
|
||||
|
||||
|
||||
|
||||
Perez-Mendez, et al. Expires March 5, 2013 [Page 6]
|
||||
|
||||
Internet-Draft GSS preauth Sep 2012
|
||||
|
||||
|
||||
establishment process (GSS_S_CONTINUE_NEEDED), the KDC will reply to
|
||||
the client with a KDC_ERR_MORE_PREAUTH_DATA_REQUIRED error message
|
||||
[I-D.ietf-krb-wg-preauth-framework]. In the e-data field of the
|
||||
message, the KDC will include the PA-GSS padata, containing both, the
|
||||
GSS token (sec-ctx-token) and the exported GSS security context
|
||||
(state) (see Section 7).
|
||||
|
||||
4.4. Processing of GSS preauth responses
|
||||
|
||||
When the client receives a KDC_ERR_MORE_PREAUTH_DATA_REQUIRED error,
|
||||
it extracts the token from the PA-GSS element and invokes the
|
||||
GSS_Init_sec_context function, as described in section Section 4.1.
|
||||
If present, the state element of the PA-GSS padata is treated as an
|
||||
opaque element, and it is simply copied and included into the
|
||||
generated PA-GSS element without further processing.
|
||||
|
||||
On the other hand, when the client receives a KRB_AS_REP, it knows
|
||||
the context establishment has finalized successfully. The client
|
||||
invokes the GSS_Init_sec_context function using the transported GSS
|
||||
token. Note that, to be consistent, this call MUST return
|
||||
GSS_S_COMPLETE and not generate any output_token, since the KDC does
|
||||
not expect further data from the client.
|
||||
|
||||
If the context establishment is completed correctly, the client MUST
|
||||
use the same key derivation process followed by the KDC (Section 4.3)
|
||||
to obtain the reply key to decrypt the enc-part of the KRB_AS_REP.
|
||||
|
||||
|
||||
5. Data in the KDC_ERR_PREAUTH_REQUIRED
|
||||
|
||||
When the KDC sends a KDC_ERR_PREAUTH_REQUIRED error to the client, it
|
||||
includes a sequence of padata, each corresponding to an acceptable
|
||||
pre-authentication method. Optionally, these padata elements contain
|
||||
data valuable for the client to configure the selected mechanism.
|
||||
The data to be included in the padata for this message is described
|
||||
in this section.
|
||||
|
||||
TBD. (For example, list of the OIDs of the GSS mechanisms supported
|
||||
by the KDC)
|
||||
|
||||
|
||||
6. Derivation of the reply key from the GSS context
|
||||
|
||||
The GSS pre-authentication mechanism proposed in this draft provides
|
||||
the "Replacing-reply-key" facility
|
||||
[I-D.ietf-krb-wg-preauth-framework].
|
||||
|
||||
After a successful authentication, client and KDC may decide to
|
||||
|
||||
|
||||
|
||||
Perez-Mendez, et al. Expires March 5, 2013 [Page 7]
|
||||
|
||||
Internet-Draft GSS preauth Sep 2012
|
||||
|
||||
|
||||
completely replace the reply key used to encrypt the KRB_AS_REP by a
|
||||
new one that is cryptographically independent from the client's
|
||||
password stored in client password on the Kerberos users database.
|
||||
This additional keying material can be obtained by means of calls to
|
||||
the GSS_Pseudo_random [RFC4401] function, using "KRB-GSS" as the
|
||||
prf_in parameter.
|
||||
|
||||
|
||||
7. KDC state management
|
||||
|
||||
The Kerberos standard [RFC4120] defines the KDC as a stateless
|
||||
entity. This means that, if the GSS mechanism requires more than one
|
||||
round-trip, the client MUST provide enough data to the KDC in the
|
||||
following interactions to allow recovering the complete state of the
|
||||
ongoing authentication. This is specially relevant when the client
|
||||
switches from one KDC to different one (within the same realm) during
|
||||
a pre-authentication process. This second KDC must be able to
|
||||
continue with the process in a seamless way.
|
||||
|
||||
The GSS-API manages the so-called security contexts. They represent
|
||||
the whole context of an authentication, including all the state and
|
||||
relevant data of the ongoing security context. Thus, this
|
||||
information MUST be serialized and sent to the client in order to
|
||||
ensure that the KDC receiving it will be able to reconstruct the
|
||||
associated state. In order to prevent attacks, this information must
|
||||
be confidentiality and integrity protected using a key shared amongst
|
||||
all the KDCs deployed in the realm, and must be sent along with a
|
||||
timestamp to prevent replay attacks. How this information is encoded
|
||||
is described in section Section 3.
|
||||
|
||||
To generate the serialized security context information, the
|
||||
GSS_Export_sec_ctx() call is used. The main drawback of this
|
||||
approach is that the current GSS-API specifications does not allow
|
||||
the exportation of a security context which has not been completely
|
||||
established. Nevertheless, some GSS mechanisms do allow the
|
||||
exportation of partially established context (e.g.
|
||||
[I-D.ietf-abfab-gss-eap]), and we expect that other GSS mechanisms
|
||||
will do the same in the future.
|
||||
|
||||
|
||||
8. Support for federated users
|
||||
|
||||
This draft supports the authentication of users belonging to a
|
||||
different domain than the authenticating KDC. This is achieved by
|
||||
letting the GSS-API to provide both, the client name and the reply
|
||||
key to be used. That means that the requested username may not be
|
||||
present in the KDC's database. To avoid the generation of an error
|
||||
of type KDC_ERR_C_PRINCIPAL_UNKNOWN, when the Kerberos client knows
|
||||
|
||||
|
||||
|
||||
Perez-Mendez, et al. Expires March 5, 2013 [Page 8]
|
||||
|
||||
Internet-Draft GSS preauth Sep 2012
|
||||
|
||||
|
||||
it is operating in a federated environment, it MUST set the value of
|
||||
the cname field of the KRB_AS_REQ message to a new wellknown value,
|
||||
WELLKNOWN/FEDERATED, following the model proposed in [RFC6111]. In
|
||||
this way, the KDC will be completely authenticated by the GSS-API
|
||||
calls, and thus no local verification of credentials should be done.
|
||||
|
||||
|
||||
9. GSS channel bindings
|
||||
|
||||
In order to link the GSS authentication with the actual Kerberos
|
||||
exchange transporting GSS tokens, the DER-encoded KDC-REQ-BODY from
|
||||
the AS-REQ is used as channel bindings.
|
||||
|
||||
|
||||
10. Acknowledgements
|
||||
|
||||
This work is supported by the project MULTIGIGABIT EUROPEAN ACADEMIC
|
||||
NETWORK (FP7-INFRASTRUCTURES-2009-1). It is also funded by a Seneca
|
||||
Foundation grant from the Human Resources Researching Training
|
||||
Program 2007. Authors finally thank the Funding Program for Research
|
||||
Groups of Excellence with code 04552/GERM/06 granted by the Fundacion
|
||||
Seneca.
|
||||
|
||||
|
||||
11. Security Considerations
|
||||
|
||||
Protection of Request/Responses with FAST, restriction on GSS
|
||||
mechanism, etc. TBD.
|
||||
|
||||
|
||||
12. IANA Considerations
|
||||
|
||||
This document has no actions for IANA.
|
||||
|
||||
|
||||
13. Normative References
|
||||
|
||||
[I-D.ietf-abfab-gss-eap]
|
||||
Hartman, S. and J. Howlett, "A GSS-API Mechanism for the
|
||||
Extensible Authentication Protocol",
|
||||
draft-ietf-abfab-gss-eap-09 (work in progress),
|
||||
August 2012.
|
||||
|
||||
[I-D.ietf-krb-wg-preauth-framework]
|
||||
Hartman, S. and L. Zhu, "A Generalized Framework for
|
||||
Kerberos Pre-Authentication",
|
||||
draft-ietf-krb-wg-preauth-framework-17 (work in progress),
|
||||
June 2010.
|
||||
|
||||
|
||||
|
||||
Perez-Mendez, et al. Expires March 5, 2013 [Page 9]
|
||||
|
||||
Internet-Draft GSS preauth Sep 2012
|
||||
|
||||
|
||||
[I-D.perez-abfab-eap-gss-preauth]
|
||||
Perez-Mendez, A., Lopez, R., Pereniguez-Garcia, F., and G.
|
||||
Lopez-Millan, "GSS-EAP pre-authentication for Kerberos",
|
||||
draft-perez-abfab-eap-gss-preauth-01 (work in progress),
|
||||
March 2012.
|
||||
|
||||
[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.
|
||||
|
||||
[RFC4120] Neuman, C., Yu, T., Hartman, S., and K. Raeburn, "The
|
||||
Kerberos Network Authentication Service (V5)", RFC 4120,
|
||||
July 2005.
|
||||
|
||||
[RFC4401] Williams, N., "A Pseudo-Random Function (PRF) API
|
||||
Extension for the Generic Security Service Application
|
||||
Program Interface (GSS-API)", RFC 4401, February 2006.
|
||||
|
||||
[RFC4556] Zhu, L. and B. Tung, "Public Key Cryptography for Initial
|
||||
Authentication in Kerberos (PKINIT)", RFC 4556, June 2006.
|
||||
|
||||
[RFC6111] Zhu, L., "Additional Kerberos Naming Constraints",
|
||||
RFC 6111, April 2011.
|
||||
|
||||
|
||||
Authors' Addresses
|
||||
|
||||
Alejandro Perez-Mendez (Ed.)
|
||||
University of Murcia
|
||||
Campus de Espinardo S/N, Faculty of Computer Science
|
||||
Murcia, 30100
|
||||
Spain
|
||||
|
||||
Phone: +34 868 88 46 44
|
||||
Email: alex@um.es
|
||||
|
||||
|
||||
Rafa Marin-Lopez
|
||||
University of Murcia
|
||||
Campus de Espinardo S/N, Faculty of Computer Science
|
||||
Murcia, 30100
|
||||
Spain
|
||||
|
||||
Phone: +34 868 88 85 01
|
||||
Email: rafa@um.es
|
||||
|
||||
|
||||
|
||||
|
||||
Perez-Mendez, et al. Expires March 5, 2013 [Page 10]
|
||||
|
||||
Internet-Draft GSS preauth Sep 2012
|
||||
|
||||
|
||||
Fernando Pereniguez-Garcia
|
||||
University of Murcia
|
||||
Campus de Espinardo S/N, Faculty of Computer Science
|
||||
Murcia, 30100
|
||||
Spain
|
||||
|
||||
Phone: +34 868 88 78 82
|
||||
Email: pereniguez@um.es
|
||||
|
||||
|
||||
Gabriel Lopez-Millan
|
||||
University of Murcia
|
||||
Campus de Espinardo S/N, Faculty of Computer Science
|
||||
Murcia, 30100
|
||||
Spain
|
||||
|
||||
Phone: +34 868 88 85 04
|
||||
Email: gabilm@um.es
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Perez-Mendez, et al. Expires March 5, 2013 [Page 11]
|
||||
|
@@ -236,6 +236,12 @@ init(struct init_options *opt, int argc, char **argv)
|
||||
KRB5_KDB_REQUIRES_PRE_AUTH, 0);
|
||||
krb5_free_principal(context, princ);
|
||||
|
||||
/* Create `WELLKNOWN/FEDERATED' for GSS preauth */
|
||||
krb5_make_principal(context, &princ, realm,
|
||||
KRB5_WELLKNOWN_NAME, KRB5_FEDERATED_NAME, NULL);
|
||||
create_random_entry(princ, 60*60, 60*60,
|
||||
KRB5_KDB_REQUIRES_PRE_AUTH, 0);
|
||||
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,
|
||||
|
@@ -117,6 +117,7 @@ libkdc_la_SOURCES = \
|
||||
csr_authorizer.c \
|
||||
process.c \
|
||||
windc.c \
|
||||
gss_preauth.c \
|
||||
rx.h
|
||||
|
||||
KDC_PROTOS = $(srcdir)/kdc-protos.h $(srcdir)/kdc-private.h
|
||||
@@ -185,6 +186,7 @@ libkdc_la_LIBADD = \
|
||||
$(LIB_pkinit) \
|
||||
$(top_builddir)/lib/hdb/libhdb.la \
|
||||
$(top_builddir)/lib/krb5/libkrb5.la \
|
||||
$(top_builddir)/lib/gssapi/libgssapi.la \
|
||||
$(LIB_kdb) \
|
||||
$(top_builddir)/lib/ntlm/libheimntlm.la \
|
||||
$(LIB_hcrypto) \
|
||||
|
@@ -107,7 +107,8 @@ LIBKDC_OBJS=\
|
||||
$(OBJ)\token_validator.obj \
|
||||
$(OBJ)\csr_authorizer.obj \
|
||||
$(OBJ)\process.obj \
|
||||
$(OBJ)\windc.obj
|
||||
$(OBJ)\windc.obj \
|
||||
$(OBJ)\gss_preauth.obj
|
||||
|
||||
LIBKDC_LIBS=\
|
||||
$(LIBHDB) \
|
||||
@@ -146,6 +147,7 @@ libkdc_la_SOURCES = \
|
||||
csr_authorizer.c \
|
||||
process.c \
|
||||
windc.c \
|
||||
gss_preauth.c \
|
||||
rx.h
|
||||
|
||||
$(OBJ)\kdc-protos.h: $(libkdc_la_SOURCES)
|
||||
|
@@ -74,6 +74,7 @@ krb5_kdc_get_config(krb5_context context, krb5_kdc_configuration **config)
|
||||
{
|
||||
static heim_base_once_t load_kdc_plugins = HEIM_BASE_ONCE_INIT;
|
||||
krb5_kdc_configuration *c;
|
||||
krb5_error_code ret;
|
||||
|
||||
heim_base_once_f(&load_kdc_plugins, context, load_kdc_plugins_once);
|
||||
|
||||
@@ -329,6 +330,36 @@ krb5_kdc_get_config(krb5_context context, krb5_kdc_configuration **config)
|
||||
"synthetic_clients_max_renew",
|
||||
NULL);
|
||||
|
||||
c->enable_gss_preauth =
|
||||
krb5_config_get_bool_default(context, NULL,
|
||||
c->enable_gss_preauth,
|
||||
"kdc",
|
||||
"enable_gss_preauth", NULL);
|
||||
|
||||
c->enable_gss_auth_data =
|
||||
krb5_config_get_bool_default(context, NULL,
|
||||
c->enable_gss_auth_data,
|
||||
"kdc",
|
||||
"enable_gss_auth_data", NULL);
|
||||
|
||||
ret = _kdc_gss_get_mechanism_config(context, "kdc",
|
||||
"gss_mechanisms_allowed",
|
||||
&c->gss_mechanisms_allowed);
|
||||
if (ret) {
|
||||
free(c);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = _kdc_gss_get_mechanism_config(context, "kdc",
|
||||
"gss_cross_realm_mechanisms_allowed",
|
||||
&c->gss_cross_realm_mechanisms_allowed);
|
||||
if (ret) {
|
||||
OM_uint32 minor;
|
||||
gss_release_oid_set(&minor, &c->gss_mechanisms_allowed);
|
||||
free(c);
|
||||
return ret;
|
||||
}
|
||||
|
||||
*config = c;
|
||||
|
||||
return 0;
|
||||
|
@@ -42,6 +42,7 @@
|
||||
#include <getarg.h>
|
||||
|
||||
typedef struct pk_client_params pk_client_params;
|
||||
typedef struct gss_client_params gss_client_params;
|
||||
struct DigestREQ;
|
||||
struct Kx509Request;
|
||||
|
||||
|
65
kdc/fast.c
65
kdc/fast.c
@@ -257,6 +257,20 @@ _kdc_fast_mk_error(astgs_request_t r,
|
||||
|
||||
krb5_data_zero(&e_data);
|
||||
|
||||
if (armor_crypto || (r && r->fast.fast_state.len)) {
|
||||
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, 1, "failed to add fast cookie with: %d", ret);
|
||||
free_METHOD_DATA(error_method);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
if (armor_crypto) {
|
||||
PA_FX_FAST_REPLY fxfastrep;
|
||||
KrbFastResponse fastrep;
|
||||
@@ -292,18 +306,6 @@ _kdc_fast_mk_error(astgs_request_t r,
|
||||
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, 1, "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);
|
||||
@@ -342,8 +344,8 @@ _kdc_fast_mk_error(astgs_request_t r,
|
||||
return ret;
|
||||
}
|
||||
|
||||
krb5_error_code
|
||||
_kdc_fast_unwrap_request(astgs_request_t r)
|
||||
static krb5_error_code
|
||||
fast_unwrap_request(astgs_request_t r)
|
||||
{
|
||||
krb5_principal armor_server = NULL;
|
||||
hdb_entry_ex *armor_user = NULL;
|
||||
@@ -362,17 +364,6 @@ _kdc_fast_unwrap_request(astgs_request_t r)
|
||||
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;
|
||||
@@ -562,6 +553,30 @@ _kdc_fast_unwrap_request(astgs_request_t r)
|
||||
return ret;
|
||||
}
|
||||
|
||||
krb5_error_code
|
||||
_kdc_fast_unwrap_request(astgs_request_t r)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
const PA_DATA *pa;
|
||||
int i = 0;
|
||||
|
||||
ret = fast_unwrap_request(r);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* Non-FAST mechanisms may use FX-COOKIE to manage state.
|
||||
*/
|
||||
pa = _kdc_find_padata(&r->req, &i, KRB5_PADATA_FX_COOKIE);
|
||||
if (pa) {
|
||||
ret = fast_parse_cookie(r, pa);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
_kdc_free_fast_state(KDCFastState *state)
|
||||
{
|
||||
|
804
kdc/gss_preauth.c
Normal file
804
kdc/gss_preauth.c
Normal file
@@ -0,0 +1,804 @@
|
||||
/*
|
||||
* Copyright (c) 2021, PADL Software Pty Ltd.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Portions Copyright (c) 2019 Kungliga Tekniska Högskolan
|
||||
*
|
||||
* 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 PADL Software 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 PADL SOFTWARE 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 PADL SOFTWARE 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"
|
||||
|
||||
#include <gssapi/gssapi.h>
|
||||
#include <gssapi_mech.h>
|
||||
#include "../lib/gssapi/preauth/pa-private.h"
|
||||
|
||||
#include "gss_preauth_authorizer_plugin.h"
|
||||
|
||||
struct gss_client_params {
|
||||
gss_ctx_id_t context_handle;
|
||||
gss_name_t initiator_name;
|
||||
gss_OID mech_type;
|
||||
gss_buffer_desc output_token;
|
||||
OM_uint32 flags;
|
||||
OM_uint32 lifetime;
|
||||
};
|
||||
|
||||
static void
|
||||
pa_gss_display_status(astgs_request_t r,
|
||||
OM_uint32 major,
|
||||
OM_uint32 minor,
|
||||
gss_client_params *gcp,
|
||||
const char *msg);
|
||||
|
||||
static void
|
||||
pa_gss_display_name(gss_name_t name,
|
||||
gss_buffer_t namebuf,
|
||||
gss_const_buffer_t *namebuf_p);
|
||||
|
||||
/*
|
||||
* Deserialize a GSS-API security context from the FAST cookie.
|
||||
*/
|
||||
static krb5_error_code
|
||||
pa_gss_get_context_state(astgs_request_t r,
|
||||
gss_client_params *gcp)
|
||||
{
|
||||
int idx = 0;
|
||||
PA_DATA *fast_pa;
|
||||
|
||||
fast_pa = krb5_find_padata(r->fast.fast_state.val,
|
||||
r->fast.fast_state.len,
|
||||
KRB5_PADATA_GSS, &idx);
|
||||
if (fast_pa) {
|
||||
gss_buffer_desc sec_context_token;
|
||||
OM_uint32 major, minor;
|
||||
|
||||
_krb5_gss_data_to_buffer(&fast_pa->padata_value, &sec_context_token);
|
||||
major = gss_import_sec_context(&minor, &sec_context_token,
|
||||
&gcp->context_handle);
|
||||
if (GSS_ERROR(major))
|
||||
pa_gss_display_status(r, major, minor, gcp,
|
||||
"Failed to import GSS pre-authentication context");
|
||||
|
||||
return _krb5_gss_map_error(major, minor);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Serialize a GSS-API security context into a FAST cookie.
|
||||
*/
|
||||
static krb5_error_code
|
||||
pa_gss_set_context_state(astgs_request_t r,
|
||||
gss_client_params *gcp)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
PA_DATA *fast_pa;
|
||||
int idx = 0;
|
||||
|
||||
OM_uint32 major, minor;
|
||||
gss_buffer_desc sec_context_token = GSS_C_EMPTY_BUFFER;
|
||||
|
||||
major = gss_export_sec_context(&minor, &gcp->context_handle,
|
||||
&sec_context_token);
|
||||
if (GSS_ERROR(major)) {
|
||||
pa_gss_display_status(r, major, minor, gcp,
|
||||
"Failed to export GSS pre-authentication context");
|
||||
return _krb5_gss_map_error(major, minor);
|
||||
}
|
||||
|
||||
fast_pa = krb5_find_padata(r->fast.fast_state.val,
|
||||
r->fast.fast_state.len,
|
||||
KRB5_PADATA_GSS, &idx);
|
||||
if (fast_pa) {
|
||||
krb5_data_free(&fast_pa->padata_value);
|
||||
_krb5_gss_buffer_to_data(&sec_context_token, &fast_pa->padata_value);
|
||||
} else {
|
||||
ret = krb5_padata_add(r->context,
|
||||
&r->fast.fast_state,
|
||||
KRB5_PADATA_GSS,
|
||||
sec_context_token.value,
|
||||
sec_context_token.length);
|
||||
if (ret) {
|
||||
gss_release_buffer(&minor, &sec_context_token);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static krb5_error_code
|
||||
pa_gss_acquire_acceptor_cred(astgs_request_t r,
|
||||
gss_client_params *gcp,
|
||||
gss_cred_id_t *cred)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
|
||||
OM_uint32 major, minor;
|
||||
gss_name_t target_name = GSS_C_NO_NAME;
|
||||
gss_buffer_desc display_name = GSS_C_EMPTY_BUFFER;
|
||||
gss_const_buffer_t display_name_p;
|
||||
|
||||
*cred = GSS_C_NO_CREDENTIAL;
|
||||
|
||||
ret = _krb5_gss_pa_unparse_name(r->context, r->server_princ, &target_name);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
pa_gss_display_name(target_name, &display_name, &display_name_p);
|
||||
|
||||
kdc_log(r->context, r->config, 4,
|
||||
"Acquiring GSS acceptor credential for %.*s",
|
||||
(int)display_name_p->length, (char *)display_name_p->value);
|
||||
|
||||
major = gss_acquire_cred(&minor, target_name, GSS_C_INDEFINITE,
|
||||
r->config->gss_mechanisms_allowed,
|
||||
GSS_C_ACCEPT, cred, NULL, NULL);
|
||||
ret = _krb5_gss_map_error(major, minor);
|
||||
|
||||
if (ret)
|
||||
pa_gss_display_status(r, major, minor, gcp,
|
||||
"Failed to acquire GSS acceptor credential");
|
||||
|
||||
gss_release_buffer(&minor, &display_name);
|
||||
gss_release_name(&minor, &target_name);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
krb5_error_code
|
||||
_kdc_gss_rd_padata(astgs_request_t r,
|
||||
const PA_DATA *pa,
|
||||
gss_client_params **pgcp,
|
||||
int *open)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
size_t size;
|
||||
|
||||
OM_uint32 major, minor;
|
||||
gss_client_params *gcp = NULL;
|
||||
gss_cred_id_t cred = GSS_C_NO_CREDENTIAL;
|
||||
gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
|
||||
struct gss_channel_bindings_struct cb;
|
||||
|
||||
memset(&cb, 0, sizeof(cb));
|
||||
|
||||
*pgcp = NULL;
|
||||
|
||||
if (!r->config->enable_gss_preauth) {
|
||||
ret = KRB5KDC_ERR_POLICY;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (pa->padata_value.length == 0) {
|
||||
ret = KRB5KDC_ERR_PREAUTH_FAILED;
|
||||
goto out;
|
||||
}
|
||||
|
||||
gcp = calloc(1, sizeof(*gcp));
|
||||
if (gcp == NULL) {
|
||||
ret = krb5_enomem(r->context);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = pa_gss_get_context_state(r, gcp);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = pa_gss_acquire_acceptor_cred(r, gcp, &cred);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
_krb5_gss_data_to_buffer(&pa->padata_value, &input_token);
|
||||
|
||||
ASN1_MALLOC_ENCODE(KDC_REQ_BODY, cb.application_data.value,
|
||||
cb.application_data.length, &r->req.req_body,
|
||||
&size, ret);
|
||||
heim_assert(ret || size == cb.application_data.length,
|
||||
"internal asn1 encoder error");
|
||||
|
||||
major = gss_accept_sec_context(&minor,
|
||||
&gcp->context_handle,
|
||||
cred,
|
||||
&input_token,
|
||||
&cb,
|
||||
&gcp->initiator_name,
|
||||
&gcp->mech_type,
|
||||
&gcp->output_token,
|
||||
&gcp->flags,
|
||||
&gcp->lifetime,
|
||||
NULL); /* delegated_cred_handle */
|
||||
if (GSS_ERROR(major)) {
|
||||
pa_gss_display_status(r, major, minor, gcp,
|
||||
"Failed to accept GSS security context");
|
||||
ret = _krb5_gss_map_error(major, minor);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if ((gcp->flags & GSS_C_ANON_FLAG) && !_kdc_is_anon_request(&r->req)) {
|
||||
kdc_log(r->context, r->config, 2,
|
||||
"Anonymous GSS pre-authentication request w/o anonymous flag");
|
||||
ret = KRB5KDC_ERR_BADOPTION;
|
||||
goto out;
|
||||
}
|
||||
|
||||
*open = (major == GSS_S_COMPLETE);
|
||||
|
||||
out:
|
||||
gss_release_cred(&minor, &cred);
|
||||
gss_release_buffer(&minor, &cb.application_data);
|
||||
|
||||
if (ret == 0)
|
||||
*pgcp = gcp;
|
||||
else
|
||||
_kdc_gss_free_client_param(r, gcp);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
krb5_timestamp
|
||||
_kdc_gss_endtime(astgs_request_t r,
|
||||
gss_client_params *gcp)
|
||||
{
|
||||
krb5_timestamp endtime;
|
||||
|
||||
if (gcp->lifetime == GSS_C_INDEFINITE)
|
||||
endtime = 0;
|
||||
else
|
||||
endtime = kdc_time + gcp->lifetime;
|
||||
|
||||
kdc_log(r->context, r->config, 10,
|
||||
"GSS pre-authentication endtime is %ld", endtime);
|
||||
|
||||
return endtime;
|
||||
}
|
||||
|
||||
struct pa_gss_plugin_ctx {
|
||||
astgs_request_t r;
|
||||
struct gss_client_params *gcp;
|
||||
krb5_boolean authorized;
|
||||
krb5_principal initiator_princ;
|
||||
};
|
||||
|
||||
static krb5_error_code
|
||||
pa_gss_authorize_cb(krb5_context context,
|
||||
const void *plug,
|
||||
void *plugctx,
|
||||
void *userctx)
|
||||
{
|
||||
const krb5plugin_gss_preauth_authorizer_ftable *authorizer = plug;
|
||||
struct pa_gss_plugin_ctx *pa_gss_plugin_ctx = userctx;
|
||||
|
||||
return authorizer->authorize(plugctx, context,
|
||||
&pa_gss_plugin_ctx->r->req,
|
||||
pa_gss_plugin_ctx->r->client_princ,
|
||||
pa_gss_plugin_ctx->r->client,
|
||||
pa_gss_plugin_ctx->gcp->initiator_name,
|
||||
pa_gss_plugin_ctx->gcp->mech_type,
|
||||
pa_gss_plugin_ctx->gcp->flags,
|
||||
&pa_gss_plugin_ctx->authorized,
|
||||
&pa_gss_plugin_ctx->initiator_princ);
|
||||
}
|
||||
|
||||
static const char *plugin_deps[] = {
|
||||
"kdc",
|
||||
"hdb",
|
||||
"gssapi",
|
||||
"krb5",
|
||||
NULL
|
||||
};
|
||||
|
||||
static struct heim_plugin_data
|
||||
gss_preauth_authorizer_data = {
|
||||
"kdc",
|
||||
KDC_GSS_PREAUTH_AUTHORIZER,
|
||||
KDC_GSS_PREAUTH_AUTHORIZER_VERSION_0,
|
||||
plugin_deps,
|
||||
kdc_get_instance
|
||||
};
|
||||
|
||||
static krb5_error_code
|
||||
pa_gss_authorize_plugin(astgs_request_t r,
|
||||
struct gss_client_params *gcp,
|
||||
gss_const_buffer_t display_name,
|
||||
krb5_boolean *authorized,
|
||||
krb5_principal *initiator_princ)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
struct pa_gss_plugin_ctx ctx;
|
||||
|
||||
ctx.r = r;
|
||||
ctx.gcp = gcp;
|
||||
ctx.authorized = 0;
|
||||
ctx.initiator_princ = NULL;
|
||||
|
||||
krb5_clear_error_message(r->context);
|
||||
ret = _krb5_plugin_run_f(r->context, &gss_preauth_authorizer_data,
|
||||
0, &ctx, pa_gss_authorize_cb);
|
||||
|
||||
if (ret != KRB5_PLUGIN_NO_HANDLE) {
|
||||
const char *msg = krb5_get_error_message(r->context, ret);
|
||||
|
||||
kdc_log(r->context, r->config, 7,
|
||||
"GSS authz plugin %sauthorize%s %s initiator %.*s: %s",
|
||||
ctx.authorized ? "" : "did not " ,
|
||||
ctx.authorized ? "d" : "",
|
||||
gss_oid_to_name(gcp->mech_type),
|
||||
(int)display_name->length, (char *)display_name->value,
|
||||
msg);
|
||||
krb5_free_error_message(r->context, msg);
|
||||
}
|
||||
|
||||
*authorized = ctx.authorized;
|
||||
*initiator_princ = ctx.initiator_princ;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static krb5_error_code
|
||||
pa_gss_authorize_default(astgs_request_t r,
|
||||
struct gss_client_params *gcp,
|
||||
gss_const_buffer_t display_name,
|
||||
krb5_boolean *authorized,
|
||||
krb5_principal *initiator_princ)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
krb5_principal principal;
|
||||
krb5_const_realm realm = r->server->entry.principal->realm;
|
||||
int flags = 0, cross_realm_allowed = 0, unauth_anon;
|
||||
|
||||
/*
|
||||
* gss_cross_realm_mechanisms_allowed is a list of GSS-API mechanisms
|
||||
* that are allowed to map directly to Kerberos principals in any
|
||||
* realm. If the authenticating mechanism is not on the list, then
|
||||
* the initiator will be mapped to an enterprise principal in the
|
||||
* service realm. This is useful to stop synthetic principals in
|
||||
* foreign realms being conflated with true cross-realm principals.
|
||||
*/
|
||||
if (r->config->gss_cross_realm_mechanisms_allowed) {
|
||||
OM_uint32 minor;
|
||||
|
||||
gss_test_oid_set_member(&minor, gcp->mech_type,
|
||||
r->config->gss_cross_realm_mechanisms_allowed,
|
||||
&cross_realm_allowed);
|
||||
}
|
||||
|
||||
kdc_log(r->context, r->config, 10,
|
||||
"Initiator %.*s will be mapped to %s",
|
||||
(int)display_name->length, (char *)display_name->value,
|
||||
cross_realm_allowed ? "nt-principal" : "nt-enterprise-principal");
|
||||
|
||||
if (!cross_realm_allowed)
|
||||
flags |= KRB5_PRINCIPAL_PARSE_ENTERPRISE | KRB5_PRINCIPAL_PARSE_NO_REALM;
|
||||
|
||||
ret = _krb5_gss_pa_parse_name(r->context, gcp->initiator_name,
|
||||
flags, &principal);
|
||||
if (ret) {
|
||||
const char *msg = krb5_get_error_message(r->context, ret);
|
||||
|
||||
kdc_log(r->context, r->config, 2,
|
||||
"Failed to parse %s initiator name %.*s: %s",
|
||||
gss_oid_to_name(gcp->mech_type),
|
||||
(int)display_name->length, (char *)display_name->value, msg);
|
||||
krb5_free_error_message(r->context, msg);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* GSS_C_ANON_FLAG indicates the client requested anonymous authentication
|
||||
* (it is validated against the request-anonymous flag).
|
||||
*
|
||||
* _kdc_is_anonymous_pkinit() returns TRUE if the principal contains both
|
||||
* the well known anonymous name and realm.
|
||||
*/
|
||||
unauth_anon = (gcp->flags & GSS_C_ANON_FLAG) &&
|
||||
_kdc_is_anonymous_pkinit(r->context, principal);
|
||||
|
||||
/*
|
||||
* Always use the anonymous entry created in our HDB, i.e. with the local
|
||||
* realm, for authorizing anonymous requests. This matches PKINIT behavior
|
||||
* as anonymous PKINIT requests include the KDC realm in the request.
|
||||
*/
|
||||
if (unauth_anon || (flags & KRB5_PRINCIPAL_PARSE_ENTERPRISE)) {
|
||||
ret = krb5_principal_set_realm(r->context, principal, realm);
|
||||
if (ret) {
|
||||
krb5_free_principal(r->context, principal);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
if (unauth_anon) {
|
||||
/*
|
||||
* Special case to avoid changing _kdc_as_rep(). If the initiator is
|
||||
* the unauthenticated anonymous principal, r->client_princ also needs
|
||||
* to be set in order to force the AS-REP realm to be set to the well-
|
||||
* known anonymous identity. This is because (unlike anonymous PKINIT)
|
||||
* we only require the anonymous flag, not the anonymous name, in the
|
||||
* client AS-REQ.
|
||||
*/
|
||||
krb5_principal anon_princ;
|
||||
|
||||
ret = krb5_copy_principal(r->context, principal, &anon_princ);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
krb5_free_principal(r->context, r->client_princ);
|
||||
r->client_princ = anon_princ;
|
||||
}
|
||||
|
||||
*authorized = TRUE;
|
||||
*initiator_princ = principal;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
krb5_error_code
|
||||
_kdc_gss_check_client(astgs_request_t r,
|
||||
gss_client_params *gcp,
|
||||
char **client_name)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
krb5_principal initiator_princ = NULL;
|
||||
hdb_entry_ex *initiator = NULL;
|
||||
krb5_boolean authorized = FALSE;
|
||||
|
||||
OM_uint32 minor;
|
||||
gss_buffer_desc display_name = GSS_C_EMPTY_BUFFER;
|
||||
gss_const_buffer_t display_name_p;
|
||||
|
||||
*client_name = NULL;
|
||||
|
||||
pa_gss_display_name(gcp->initiator_name, &display_name, &display_name_p);
|
||||
|
||||
/*
|
||||
* If no plugins handled the authorization request, then all clients
|
||||
* are authorized as the directly corresponding Kerberos principal.
|
||||
*/
|
||||
ret = pa_gss_authorize_plugin(r, gcp, display_name_p,
|
||||
&authorized, &initiator_princ);
|
||||
if (ret == KRB5_PLUGIN_NO_HANDLE)
|
||||
ret = pa_gss_authorize_default(r, gcp, display_name_p,
|
||||
&authorized, &initiator_princ);
|
||||
if (ret == 0 && !authorized)
|
||||
ret = KRB5_KDC_ERR_CLIENT_NAME_MISMATCH;
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = krb5_unparse_name(r->context, initiator_princ, client_name);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
kdc_log(r->context, r->config, 4,
|
||||
"Mapped GSS %s initiator %.*s to principal %s",
|
||||
gss_oid_to_name(gcp->mech_type),
|
||||
(int)display_name_p->length, (char *)display_name_p->value,
|
||||
*client_name);
|
||||
|
||||
ret = _kdc_db_fetch(r->context,
|
||||
r->config,
|
||||
initiator_princ,
|
||||
HDB_F_FOR_AS_REQ | HDB_F_GET_CLIENT |
|
||||
HDB_F_CANON | HDB_F_SYNTHETIC_OK,
|
||||
NULL,
|
||||
&r->clientdb,
|
||||
&initiator);
|
||||
if (ret) {
|
||||
const char *msg = krb5_get_error_message(r->context, ret);
|
||||
|
||||
kdc_log(r->context, r->config, 4, "UNKNOWN -- %s: %s",
|
||||
*client_name, msg);
|
||||
krb5_free_error_message(r->context, msg);
|
||||
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* If the AS-REQ client name was the well-known federated name, then
|
||||
* replace the client name with the initiator name. Otherwise, the
|
||||
* two principals must match, noting that GSS pre-authentication is
|
||||
* for authentication, not general purpose impersonation.
|
||||
*/
|
||||
if (krb5_principal_is_federated(r->context, r->client->entry.principal)) {
|
||||
initiator->entry.flags.force_canonicalize = 1;
|
||||
|
||||
_kdc_free_ent(r->context, r->client);
|
||||
r->client = initiator;
|
||||
initiator = NULL;
|
||||
} else if (!krb5_principal_compare(r->context,
|
||||
r->client->entry.principal,
|
||||
initiator->entry.principal)) {
|
||||
kdc_log(r->context, r->config, 2,
|
||||
"GSS %s initiator %.*s does not match principal %s",
|
||||
gss_oid_to_name(gcp->mech_type),
|
||||
(int)display_name_p->length, (char *)display_name_p->value,
|
||||
r->cname);
|
||||
ret = KRB5_KDC_ERR_CLIENT_NAME_MISMATCH;
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
krb5_free_principal(r->context, initiator_princ);
|
||||
if (initiator)
|
||||
_kdc_free_ent(r->context, initiator);
|
||||
gss_release_buffer(&minor, &display_name);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
krb5_error_code
|
||||
_kdc_gss_mk_pa_reply(astgs_request_t r,
|
||||
gss_client_params *gcp)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
const KDC_REQ *req = &r->req;
|
||||
|
||||
OM_uint32 major, minor;
|
||||
int open;
|
||||
|
||||
major = gss_inquire_context(&minor,
|
||||
gcp->context_handle,
|
||||
NULL, /* initiator_name */
|
||||
NULL, /* target_name */
|
||||
NULL, /* lifetime_req */
|
||||
NULL, /* mech_type */
|
||||
NULL, /* ctx_flags */
|
||||
NULL, /* locally_initiated */
|
||||
&open);
|
||||
if (GSS_ERROR(major)) {
|
||||
pa_gss_display_status(r, major, minor, gcp,
|
||||
"Failed to inquire GSS context");
|
||||
ret = _krb5_gss_map_error(major, minor);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (open) {
|
||||
krb5_enctype enctype;
|
||||
uint32_t kfe = 0;
|
||||
krb5_keyblock *reply_key = NULL;
|
||||
|
||||
if (krb5_principal_is_krbtgt(r->context, r->server_princ))
|
||||
kfe |= KFE_IS_TGS;
|
||||
|
||||
ret = _kdc_find_etype(r, kfe, req->req_body.etype.val,
|
||||
req->req_body.etype.len, &enctype, NULL, NULL);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = _krb5_gss_pa_derive_key(r->context, gcp->context_handle,
|
||||
req->req_body.nonce,
|
||||
enctype, &reply_key);
|
||||
if (ret) {
|
||||
kdc_log(r->context, r->config, 10,
|
||||
"Failed to derive GSS reply key: %d", ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
krb5_free_keyblock_contents(r->context, &r->reply_key);
|
||||
r->reply_key = *reply_key;
|
||||
free(reply_key);
|
||||
} else {
|
||||
ret = pa_gss_set_context_state(r, gcp);
|
||||
if (ret)
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = krb5_padata_add(r->context, &r->outpadata, KRB5_PADATA_GSS,
|
||||
gcp->output_token.value, gcp->output_token.length);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
/* token is now owned by outpadata */
|
||||
gcp->output_token.length = 0;
|
||||
gcp->output_token.value = NULL;
|
||||
|
||||
if (!open)
|
||||
ret = KRB5_KDC_ERR_MORE_PREAUTH_DATA_REQUIRED;
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
krb5_error_code
|
||||
_kdc_gss_mk_composite_name_ad(astgs_request_t r,
|
||||
gss_client_params *gcp)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
krb5_data data;
|
||||
|
||||
OM_uint32 major, minor;
|
||||
gss_buffer_desc namebuf = GSS_C_EMPTY_BUFFER;
|
||||
|
||||
if (!r->config->enable_gss_auth_data || (gcp->flags & GSS_C_ANON_FLAG))
|
||||
return 0;
|
||||
|
||||
major = gss_export_name_composite(&minor, gcp->initiator_name, &namebuf);
|
||||
if (major == GSS_S_COMPLETE) {
|
||||
_krb5_gss_buffer_to_data(&namebuf, &data);
|
||||
|
||||
ret = _kdc_tkt_add_if_relevant_ad(r->context, &r->et,
|
||||
KRB5_AUTHDATA_GSS_COMPOSITE_NAME,
|
||||
&data);
|
||||
} else if (major != GSS_S_UNAVAILABLE)
|
||||
ret = _krb5_gss_map_error(major, minor);
|
||||
else
|
||||
ret = 0;
|
||||
|
||||
gss_release_buffer(&minor, &namebuf);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void
|
||||
_kdc_gss_free_client_param(astgs_request_t r,
|
||||
gss_client_params *gcp)
|
||||
{
|
||||
OM_uint32 minor;
|
||||
|
||||
if (gcp == NULL)
|
||||
return;
|
||||
|
||||
gss_delete_sec_context(&minor, &gcp->context_handle, GSS_C_NO_BUFFER);
|
||||
gss_release_name(&minor, &gcp->initiator_name);
|
||||
gss_release_buffer(&minor, &gcp->output_token);
|
||||
memset(gcp, 0, sizeof(*gcp));
|
||||
free(gcp);
|
||||
}
|
||||
|
||||
krb5_error_code
|
||||
_kdc_gss_get_mechanism_config(krb5_context context,
|
||||
const char *section,
|
||||
const char *key,
|
||||
gss_OID_set *oidsp)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
char **mechs, **mechp;
|
||||
|
||||
gss_OID_set oids = GSS_C_NO_OID_SET;
|
||||
OM_uint32 major, minor;
|
||||
|
||||
mechs = krb5_config_get_strings(context, NULL, section, key, NULL);
|
||||
if (mechs == NULL)
|
||||
return 0;
|
||||
|
||||
major = gss_create_empty_oid_set(&minor, &oids);
|
||||
if (GSS_ERROR(major)) {
|
||||
krb5_config_free_strings(mechs);
|
||||
return _krb5_gss_map_error(major, minor);
|
||||
}
|
||||
|
||||
for (mechp = mechs; *mechp; mechp++) {
|
||||
gss_OID oid = gss_name_to_oid(*mechp);
|
||||
if (oid == GSS_C_NO_OID)
|
||||
continue;
|
||||
|
||||
major = gss_add_oid_set_member(&minor, oid, &oids);
|
||||
if (GSS_ERROR(major))
|
||||
break;
|
||||
}
|
||||
|
||||
ret = _krb5_gss_map_error(major, minor);
|
||||
if (ret == 0)
|
||||
*oidsp = oids;
|
||||
else
|
||||
gss_release_oid_set(&minor, &oids);
|
||||
|
||||
krb5_config_free_strings(mechs);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
pa_gss_display_status(astgs_request_t r,
|
||||
OM_uint32 major,
|
||||
OM_uint32 minor,
|
||||
gss_client_params *gcp,
|
||||
const char *msg)
|
||||
{
|
||||
krb5_error_code ret = _krb5_gss_map_error(major, minor);
|
||||
gss_buffer_desc buf = GSS_C_EMPTY_BUFFER;
|
||||
OM_uint32 dmaj, dmin;
|
||||
OM_uint32 more = 0;
|
||||
char *gmmsg = NULL;
|
||||
char *gmsg = NULL;
|
||||
char *s = NULL;
|
||||
|
||||
do {
|
||||
gss_release_buffer(&dmin, &buf);
|
||||
dmaj = gss_display_status(&dmin, major, GSS_C_GSS_CODE, GSS_C_NO_OID,
|
||||
&more, &buf);
|
||||
if (GSS_ERROR(dmaj) ||
|
||||
buf.length >= INT_MAX ||
|
||||
asprintf(&s, "%s%s%.*s", gmsg ? gmsg : "", gmsg ? ": " : "",
|
||||
(int)buf.length, (char *)buf.value) == -1 ||
|
||||
s == NULL) {
|
||||
free(gmsg);
|
||||
gmsg = NULL;
|
||||
break;
|
||||
}
|
||||
gmsg = s;
|
||||
s = NULL;
|
||||
} while (!GSS_ERROR(dmaj) && more);
|
||||
|
||||
if (gcp->mech_type != GSS_C_NO_OID) {
|
||||
do {
|
||||
gss_release_buffer(&dmin, &buf);
|
||||
dmaj = gss_display_status(&dmin, major, GSS_C_MECH_CODE,
|
||||
gcp->mech_type, &more, &buf);
|
||||
if (GSS_ERROR(dmaj) ||
|
||||
asprintf(&s, "%s%s%.*s", gmmsg ? gmmsg : "", gmmsg ? ": " : "",
|
||||
(int)buf.length, (char *)buf.value) == -1 ||
|
||||
s == NULL) {
|
||||
free(gmmsg);
|
||||
gmmsg = NULL;
|
||||
break;
|
||||
}
|
||||
gmmsg = s;
|
||||
s = NULL;
|
||||
} while (!GSS_ERROR(dmaj) && more);
|
||||
}
|
||||
|
||||
if (gmsg == NULL)
|
||||
krb5_set_error_message(r->context, ENOMEM,
|
||||
"Error displaying GSS-API status");
|
||||
else
|
||||
krb5_set_error_message(r->context, ret, "%s%s%s%s", gmsg,
|
||||
gmmsg ? " (" : "", gmmsg ? gmmsg : "",
|
||||
gmmsg ? ")" : "");
|
||||
krb5_prepend_error_message(r->context, ret, "%s", msg);
|
||||
|
||||
kdc_log(r->context, r->config, 1,
|
||||
"%s: %s%s%s%s",
|
||||
msg, gmsg, gmmsg ? " (" : "", gmmsg ? gmmsg : "",
|
||||
gmmsg ? ")" : "");
|
||||
|
||||
free(gmmsg);
|
||||
free(gmsg);
|
||||
}
|
||||
|
||||
static const gss_buffer_desc
|
||||
gss_pa_unknown_display_name = {
|
||||
sizeof("<unknown name>") - 1,
|
||||
"<unknown name>"
|
||||
};
|
||||
|
||||
static void
|
||||
pa_gss_display_name(gss_name_t name,
|
||||
gss_buffer_t namebuf,
|
||||
gss_const_buffer_t *namebuf_p)
|
||||
{
|
||||
OM_uint32 major, minor;
|
||||
|
||||
major = gss_display_name(&minor, name, namebuf, NULL);
|
||||
if (GSS_ERROR(major))
|
||||
*namebuf_p = &gss_pa_unknown_display_name;
|
||||
else
|
||||
*namebuf_p = namebuf;
|
||||
}
|
78
kdc/gss_preauth_authorizer_plugin.h
Normal file
78
kdc/gss_preauth_authorizer_plugin.h
Normal file
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright (c) 2019 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.
|
||||
*/
|
||||
|
||||
#ifndef HEIMDAL_KDC_GSS_PREAUTH_AUTHORIZER_PLUGIN_H
|
||||
#define HEIMDAL_KDC_GSS_PREAUTH_AUTHORIZER_PLUGIN_H 1
|
||||
|
||||
#define KDC_GSS_PREAUTH_AUTHORIZER "kdc_gss_preauth_authorizer"
|
||||
#define KDC_GSS_PREAUTH_AUTHORIZER_VERSION_0 0
|
||||
|
||||
#include <krb5.h>
|
||||
#include <gssapi/gssapi.h>
|
||||
|
||||
/*
|
||||
* @param init Plugin initialization function (see krb5-plugin(7))
|
||||
* @param minor_version The plugin minor version number (0)
|
||||
* @param fini Plugin finalization function
|
||||
* @param authorize Plugin name authorization function
|
||||
*
|
||||
* -# plug_ctx, the context value output by the plugin's init function
|
||||
* -# context, a krb5_context
|
||||
* -# req, the AS-REQ request
|
||||
* -# client_name, the requested client principal name
|
||||
* -# client, the requested client HDB entry
|
||||
* -# initiator_name, the authenticated GSS initiator name
|
||||
* -# ret_flags, the flags returned by GSS_Init_sec_context()
|
||||
* -# authorized, indicate whether the initiator was authorized
|
||||
* -# mapped_name, the mapped principal name
|
||||
*
|
||||
* @ingroup krb5_support
|
||||
*/
|
||||
|
||||
typedef struct krb5plugin_gss_preauth_authorizer_ftable_desc {
|
||||
int minor_version;
|
||||
krb5_error_code (KRB5_LIB_CALL *init)(krb5_context, void **);
|
||||
void (KRB5_LIB_CALL *fini)(void *);
|
||||
krb5_error_code (KRB5_LIB_CALL *authorize)(void *, /*plug_ctx*/
|
||||
krb5_context, /*context*/
|
||||
KDC_REQ *, /*req*/
|
||||
krb5_const_principal,/*client_name*/
|
||||
hdb_entry_ex *, /*client*/
|
||||
gss_const_name_t, /*initiator_name*/
|
||||
gss_const_OID, /*mech_type*/
|
||||
OM_uint32, /*ret_flags*/
|
||||
krb5_boolean *, /*authorized*/
|
||||
krb5_principal *); /*mapped_name*/
|
||||
} krb5plugin_gss_preauth_authorizer_ftable;
|
||||
|
||||
#endif /* HEIMDAL_KDC_GSS_PREAUTH_AUTHORIZER_PLUGIN_H */
|
@@ -98,6 +98,7 @@
|
||||
#include <hdb.h>
|
||||
#include <hdb_err.h>
|
||||
#include <der.h>
|
||||
#include <gssapi/gssapi.h>
|
||||
|
||||
#ifndef NO_NTLM
|
||||
#include <heimntlm.h>
|
||||
|
@@ -44,6 +44,7 @@
|
||||
#include <hdb.h>
|
||||
#include <krb5.h>
|
||||
#include <kx509_asn1.h>
|
||||
#include <gssapi/gssapi.h>
|
||||
|
||||
enum krb5_kdc_trpolicy {
|
||||
TRPOLICY_ALWAYS_CHECK,
|
||||
@@ -107,6 +108,11 @@ typedef struct krb5_kdc_configuration {
|
||||
int enable_digest;
|
||||
int digests_allowed;
|
||||
|
||||
int enable_gss_preauth;
|
||||
int enable_gss_auth_data;
|
||||
gss_OID_set gss_mechanisms_allowed;
|
||||
gss_OID_set gss_cross_realm_mechanisms_allowed;
|
||||
|
||||
size_t max_datagram_reply_length;
|
||||
|
||||
int enable_kx509;
|
||||
|
@@ -41,6 +41,7 @@
|
||||
#include "headers.h"
|
||||
|
||||
typedef struct pk_client_params pk_client_params;
|
||||
typedef struct gss_client_params gss_client_params;
|
||||
|
||||
#include <kdc-private.h>
|
||||
|
||||
|
@@ -508,6 +508,56 @@ pa_pkinit_validate(astgs_request_t r, const PA_DATA *pa)
|
||||
|
||||
#endif /* PKINIT */
|
||||
|
||||
static krb5_error_code
|
||||
pa_gss_validate(astgs_request_t r, const PA_DATA *pa)
|
||||
{
|
||||
gss_client_params *gcp = NULL;
|
||||
char *client_name = NULL;
|
||||
krb5_error_code ret;
|
||||
int open;
|
||||
|
||||
ret = _kdc_gss_rd_padata(r, pa, &gcp, &open);
|
||||
if (ret) {
|
||||
_kdc_r_log(r, 4, "Failed to decode GSS PA-DATA -- %s",
|
||||
r->cname);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (open) {
|
||||
ret = _kdc_gss_check_client(r, gcp, &client_name);
|
||||
if (ret) {
|
||||
_kdc_set_e_text(r, "GSS-API client not allowed to "
|
||||
"impersonate principal");
|
||||
goto out;
|
||||
}
|
||||
|
||||
r->pa_endtime = _kdc_gss_endtime(r, gcp);
|
||||
|
||||
_kdc_r_log(r, 4, "GSS pre-authentication succeeded -- %s using %s",
|
||||
r->cname, client_name);
|
||||
free(client_name);
|
||||
|
||||
ret = _kdc_gss_mk_composite_name_ad(r, gcp);
|
||||
if (ret) {
|
||||
_kdc_set_e_text(r, "Failed to build GSS authorization data");
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
ret = _kdc_gss_mk_pa_reply(r, gcp);
|
||||
if (ret &&
|
||||
ret != KRB5_KDC_ERR_MORE_PREAUTH_DATA_REQUIRED) {
|
||||
_kdc_set_e_text(r, "Failed to build GSS pre-authentication reply");
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
if (gcp)
|
||||
_kdc_gss_free_client_param(r, gcp);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
@@ -919,7 +969,12 @@ static const struct kdc_patypes pat[] = {
|
||||
{ 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 }
|
||||
{ KRB5_PADATA_FX_COOKIE, "FX-COOKIE", 0, NULL },
|
||||
{
|
||||
KRB5_PADATA_GSS , "GSS",
|
||||
PA_ANNOUNCE | PA_SYNTHETIC_OK,
|
||||
pa_gss_validate
|
||||
},
|
||||
};
|
||||
|
||||
static void
|
||||
@@ -1808,6 +1863,20 @@ _kdc_is_anonymous(krb5_context context, krb5_const_principal principal)
|
||||
return krb5_principal_is_anonymous(context, principal, KRB5_ANON_MATCH_ANY);
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns TRUE if principal is the unauthenticated anonymous identity,
|
||||
* i.e. WELLKNOWN/ANONYMOUS@WELLKNOWN:ANONYMOUS. Unfortunately due to
|
||||
* backwards compatibility logic in krb5_principal_is_anonymous() we
|
||||
* have to use our own implementation.
|
||||
*/
|
||||
|
||||
krb5_boolean
|
||||
_kdc_is_anonymous_pkinit(krb5_context context, krb5_const_principal principal)
|
||||
{
|
||||
return _kdc_is_anonymous(context, principal) &&
|
||||
strcmp(principal->realm, KRB5_ANON_REALM) == 0;
|
||||
}
|
||||
|
||||
static int
|
||||
require_preauth_p(astgs_request_t r)
|
||||
{
|
||||
@@ -2521,7 +2590,9 @@ out:
|
||||
* In case of a non proxy error, build an error message.
|
||||
*/
|
||||
if (ret != 0 && ret != HDB_ERR_NOT_FOUND_HERE && r->reply->length == 0)
|
||||
ret = _kdc_fast_mk_error(r, &error_method,
|
||||
ret = _kdc_fast_mk_error(r,
|
||||
(ret == KRB5_KDC_ERR_MORE_PREAUTH_DATA_REQUIRED)
|
||||
? &r->outpadata : &error_method,
|
||||
r->armor_crypto,
|
||||
&req->req_body,
|
||||
ret, r->e_text,
|
||||
|
@@ -304,7 +304,9 @@ negotiate_get_instance(const char *libname)
|
||||
{
|
||||
if (strcmp(libname, "krb5") == 0)
|
||||
return krb5_get_instance(libname);
|
||||
/* XXX gss_get_instance() doesn't exist :( */
|
||||
else if (strcmp(libname, "gssapi") == 0)
|
||||
return gss_get_instance(libname);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@@ -227,6 +227,8 @@ kdc_get_instance(const char *libname)
|
||||
return hdb_get_instance(libname);
|
||||
else if (strcmp(libname, "krb5") == 0)
|
||||
return krb5_get_instance(libname);
|
||||
else if (strcmp(libname, "gssapi") == 0)
|
||||
return gss_get_instance(libname);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@@ -28,6 +28,7 @@ noinst_PROGRAMS = kverify kdecode_ticket generate-requests
|
||||
kinit_LDADD = \
|
||||
$(afs_lib) \
|
||||
$(top_builddir)/lib/krb5/libkrb5.la \
|
||||
$(top_builddir)/lib/gssapi/libgssapi.la \
|
||||
$(top_builddir)/lib/ntlm/libheimntlm.la \
|
||||
$(LIB_hcrypto) \
|
||||
$(top_builddir)/lib/asn1/libasn1.la \
|
||||
|
@@ -240,6 +240,15 @@ An example of an enterprise name is
|
||||
and this option is usually used with canonicalize so that the
|
||||
principal returned from the KDC will typically be the real principal
|
||||
name.
|
||||
.It Fl Fl gss-mech
|
||||
Enable GSS-API pre-authentication using the specified mechanism OID. Unless
|
||||
.Ar gss-name
|
||||
is also set, then the specified principal name will be used as the GSS-API
|
||||
initiator name. If the principal is specified as @REALM or left unspecified,
|
||||
then the default GSS-API credential will be used.
|
||||
.It Fl Fl gss-name
|
||||
Attempt GSS-API pre-authentication using an initiator name distinct from the
|
||||
Kerberos client principal,
|
||||
.It Fl Fl afslog
|
||||
Gets AFS tickets, converts them to version 4 format, and stores them
|
||||
in the kernel.
|
||||
|
176
kuser/kinit.c
176
kuser/kinit.c
@@ -79,6 +79,8 @@ struct hx509_certs_data *ent_user_id = NULL;
|
||||
char *pk_x509_anchors = NULL;
|
||||
int pk_use_enckey = 0;
|
||||
int pk_anon_fast_armor = 0;
|
||||
char *gss_preauth_mech = NULL;
|
||||
char *gss_preauth_name = NULL;
|
||||
static int canonicalize_flag = 0;
|
||||
static int enterprise_flag = 0;
|
||||
static int ok_as_delegate_flag = 0;
|
||||
@@ -188,6 +190,13 @@ static struct getargs args[] = {
|
||||
{ "pk-anon-fast-armor", 0, arg_flag, &pk_anon_fast_armor,
|
||||
NP_("use unauthenticated anonymous PKINIT as FAST armor", ""), NULL },
|
||||
#endif
|
||||
|
||||
{ "gss-mech", 0, arg_string, &gss_preauth_mech,
|
||||
NP_("use GSS mechanism for pre-authentication", ""), NULL },
|
||||
|
||||
{ "gss-name", 0, arg_string, &gss_preauth_name,
|
||||
NP_("use distinct GSS identity for pre-authentication", ""), NULL },
|
||||
|
||||
#ifndef NO_NTLM
|
||||
{ "ntlm-domain", 0, arg_string, &ntlm_domain,
|
||||
NP_("NTLM domain", ""), "domain" },
|
||||
@@ -570,6 +579,106 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static krb5_error_code
|
||||
make_wellknown_name(krb5_context context,
|
||||
krb5_const_realm realm,
|
||||
const char *instance,
|
||||
krb5_principal *principal)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
|
||||
ret = krb5_make_principal(context, principal, realm,
|
||||
KRB5_WELLKNOWN_NAME, instance, NULL);
|
||||
if (ret == 0)
|
||||
krb5_principal_set_type(context, *principal, KRB5_NT_WELLKNOWN);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static krb5_error_code
|
||||
acquire_gss_cred(krb5_context context,
|
||||
krb5_const_principal client,
|
||||
krb5_deltat life,
|
||||
const char *passwd,
|
||||
gss_cred_id_t *cred,
|
||||
gss_OID *mech)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
OM_uint32 major, minor;
|
||||
gss_name_t name = GSS_C_NO_NAME;
|
||||
gss_key_value_element_desc cred_element;
|
||||
gss_key_value_set_desc cred_store;
|
||||
gss_OID_set_desc mechs;
|
||||
|
||||
*cred = GSS_C_NO_CREDENTIAL;
|
||||
*mech = GSS_C_NO_OID;
|
||||
|
||||
if (gss_preauth_mech) {
|
||||
*mech = gss_name_to_oid(gss_preauth_mech);
|
||||
if (*mech == GSS_C_NO_OID)
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
if (gss_preauth_name) {
|
||||
gss_buffer_desc buf;
|
||||
|
||||
buf.value = gss_preauth_name;
|
||||
buf.length = strlen(gss_preauth_name);
|
||||
|
||||
major = gss_import_name(&minor, &buf, GSS_C_NT_USER_NAME, &name);
|
||||
ret = _krb5_gss_map_error(major, minor);
|
||||
} else if (!krb5_principal_is_federated(context, client)) {
|
||||
ret = _krb5_gss_pa_unparse_name(context, client, &name);
|
||||
} else {
|
||||
/*
|
||||
* WELLKNOWN/FEDERATED is used a placeholder where the user
|
||||
* did not specify either a Kerberos credential or a GSS-API
|
||||
* initiator name. It avoids the expense of acquiring a default
|
||||
* credential purely to interrogate the credential name.
|
||||
*/
|
||||
name = GSS_C_NO_NAME;
|
||||
ret = 0;
|
||||
}
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
cred_store.count = 1;
|
||||
cred_store.elements = &cred_element;
|
||||
|
||||
if (passwd && passwd[0]) {
|
||||
cred_element.key = "password";
|
||||
cred_element.value = passwd;
|
||||
} else if (keytab_str) {
|
||||
cred_element.key = "client_keytab",
|
||||
cred_element.value = keytab_str;
|
||||
} else {
|
||||
cred_store.count = 0;
|
||||
}
|
||||
|
||||
if (*mech) {
|
||||
mechs.count = 1;
|
||||
mechs.elements = (gss_OID)*mech;
|
||||
}
|
||||
|
||||
major = gss_acquire_cred_from(&minor,
|
||||
name,
|
||||
life ? life : GSS_C_INDEFINITE,
|
||||
*mech ? &mechs : GSS_C_NO_OID_SET,
|
||||
GSS_C_INITIATE,
|
||||
&cred_store,
|
||||
cred,
|
||||
NULL,
|
||||
NULL);
|
||||
if (major != GSS_S_COMPLETE) {
|
||||
ret = _krb5_gss_map_error(major, minor);
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
gss_release_name(&minor, &name);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifndef NO_NTLM
|
||||
|
||||
static krb5_error_code
|
||||
@@ -624,6 +733,9 @@ get_new_tickets(krb5_context context,
|
||||
krb5_init_creds_context ctx = NULL;
|
||||
krb5_get_init_creds_opt *opt = NULL;
|
||||
krb5_prompter_fct prompter = krb5_prompter_posix;
|
||||
gss_cred_id_t gss_cred = GSS_C_NO_CREDENTIAL;
|
||||
gss_OID gss_mech = GSS_C_NO_OID;
|
||||
krb5_principal federated_name = NULL;
|
||||
|
||||
#ifndef NO_NTLM
|
||||
struct ntlm_buf ntlmkey;
|
||||
@@ -775,6 +887,29 @@ get_new_tickets(krb5_context context,
|
||||
etype_str.num_strings);
|
||||
}
|
||||
|
||||
if (gss_preauth_mech || gss_preauth_name) {
|
||||
ret = acquire_gss_cred(context, principal, ticket_life,
|
||||
passwd, &gss_cred, &gss_mech);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* The principal specified on the command line is used as the GSS-API
|
||||
* initiator name, unless the --gss-name option was present, in which
|
||||
* case the initiator name is specified independently.
|
||||
*/
|
||||
if (gss_preauth_name == NULL) {
|
||||
krb5_const_realm realm = krb5_principal_get_realm(context, principal);
|
||||
|
||||
ret = make_wellknown_name(context, realm,
|
||||
KRB5_FEDERATED_NAME, &federated_name);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
principal = federated_name;
|
||||
}
|
||||
}
|
||||
|
||||
ret = krb5_init_creds_init(context, principal, prompter, NULL, start_time, opt, &ctx);
|
||||
if (ret) {
|
||||
krb5_warn(context, ret, "krb5_init_creds_init");
|
||||
@@ -816,7 +951,13 @@ get_new_tickets(krb5_context context,
|
||||
}
|
||||
}
|
||||
|
||||
if (use_keytab || keytab_str) {
|
||||
if (gss_mech != GSS_C_NO_OID) {
|
||||
ret = krb5_gss_set_init_creds(context, ctx, gss_cred, gss_mech);
|
||||
if (ret) {
|
||||
krb5_warn(context, ret, "krb5_gss_set_init_creds");
|
||||
goto out;
|
||||
}
|
||||
} else if (use_keytab || keytab_str) {
|
||||
ret = krb5_init_creds_set_keytab(context, ctx, kt);
|
||||
if (ret) {
|
||||
krb5_warn(context, ret, "krb5_init_creds_set_keytab");
|
||||
@@ -979,6 +1120,11 @@ get_new_tickets(krb5_context context,
|
||||
}
|
||||
|
||||
out:
|
||||
{
|
||||
OM_uint32 minor;
|
||||
gss_release_cred(&minor, &gss_cred);
|
||||
}
|
||||
krb5_free_principal(context, federated_name);
|
||||
krb5_get_init_creds_opt_free(context, opt);
|
||||
if (ctx)
|
||||
krb5_init_creds_free(context, ctx);
|
||||
@@ -1489,17 +1635,20 @@ main(int argc, char **argv)
|
||||
krb5_err(context, 1, ret, "krb5_pk_enterprise_certs");
|
||||
|
||||
pk_user_id = NULL;
|
||||
} else if (argc && argv[0][0] == '@' &&
|
||||
(gss_preauth_mech || anonymous_flag)) {
|
||||
const char *instance;
|
||||
|
||||
} else if (anonymous_flag && argc && argv[0][0] == '@') {
|
||||
/* If principal argument as @REALM, try anonymous PKINIT */
|
||||
if (gss_preauth_mech) {
|
||||
instance = KRB5_FEDERATED_NAME;
|
||||
} else if (anonymous_flag) {
|
||||
instance = KRB5_ANON_NAME;
|
||||
anonymous_pkinit = TRUE;
|
||||
}
|
||||
|
||||
ret = krb5_make_principal(context, &principal, &argv[0][1],
|
||||
KRB5_WELLKNOWN_NAME, KRB5_ANON_NAME,
|
||||
NULL);
|
||||
ret = make_wellknown_name(context, &argv[0][1], instance, &principal);
|
||||
if (ret)
|
||||
krb5_err(context, 1, ret, "krb5_make_principal");
|
||||
krb5_principal_set_type(context, principal, KRB5_NT_WELLKNOWN);
|
||||
anonymous_pkinit = TRUE;
|
||||
krb5_err(context, 1, ret, "make_wellknown_name");
|
||||
} else if (anonymous_flag && historical_anon_pkinit) {
|
||||
char *realm = argc == 0 ? get_default_realm(context) :
|
||||
argv[0][0] == '@' ? &argv[0][1] : argv[0];
|
||||
@@ -1512,6 +1661,15 @@ main(int argc, char **argv)
|
||||
anonymous_pkinit = TRUE;
|
||||
} else if (use_keytab || keytab_str) {
|
||||
get_princ_kt(context, &principal, argv[0]);
|
||||
} else if (gss_preauth_mech && argc == 0 && gss_preauth_name == NULL) {
|
||||
/*
|
||||
* Use the federated name as a placeholder if we have neither a Kerberos
|
||||
* nor a GSS-API client name, and we are performing GSS-API preauth.
|
||||
*/
|
||||
ret = make_wellknown_name(context, get_default_realm(context),
|
||||
KRB5_FEDERATED_NAME, &principal);
|
||||
if (ret)
|
||||
krb5_err(context, 1, ret, "make_wellknown_name");
|
||||
} else {
|
||||
get_princ(context, &principal, cred_cache, argv[0]);
|
||||
}
|
||||
|
@@ -72,6 +72,11 @@
|
||||
#include <parse_time.h>
|
||||
#include <err.h>
|
||||
#include <krb5.h>
|
||||
#include <heimbase.h>
|
||||
|
||||
#include <gssapi_mech.h>
|
||||
#include <gssapi/gssapi_preauth.h>
|
||||
#include "../lib/gssapi/preauth/pa-private.h"
|
||||
|
||||
#if defined(HAVE_SYS_IOCTL_H) && SunOS != 40
|
||||
#include <sys/ioctl.h>
|
||||
|
@@ -192,7 +192,9 @@ PADATA-TYPE ::= INTEGER {
|
||||
KRB5-PADATA-PKINIT-KX(147), -- krb-wg-anon
|
||||
KRB5-PADATA-PKU2U-NAME(148), -- zhu-pku2u
|
||||
KRB5-PADATA-REQ-ENC-PA-REP(149), --
|
||||
KRB5-PADATA-SUPPORTED-ETYPES(165) -- MS-KILE
|
||||
KRB5-PADATA-SUPPORTED-ETYPES(165), -- MS-KILE
|
||||
KRB5-PADATA-GSS(655) -- krb-wg-gss-preauth
|
||||
|
||||
}
|
||||
|
||||
AUTHDATA-TYPE ::= INTEGER {
|
||||
@@ -221,8 +223,9 @@ AUTHDATA-TYPE ::= INTEGER {
|
||||
KRB5-AUTHDATA-BEARER-TOKEN-JWT(581), -- JWT token
|
||||
KRB5-AUTHDATA-BEARER-TOKEN-SAML(582), -- SAML token
|
||||
KRB5-AUTHDATA-BEARER-TOKEN-OIDC(583), -- OIDC token
|
||||
KRB5-AUTHDATA-CSR-AUTHORIZED(584) -- Proxy has authorized client
|
||||
KRB5-AUTHDATA-CSR-AUTHORIZED(584), -- Proxy has authorized client
|
||||
-- to requested exts in CSR
|
||||
KRB5-AUTHDATA-GSS-COMPOSITE-NAME(655) -- gss_export_name_composite
|
||||
}
|
||||
|
||||
-- checksumtypes
|
||||
@@ -925,7 +928,6 @@ KERB-ARMOR-SERVICE-REPLY ::= SEQUENCE {
|
||||
armor-key [1] EncryptionKey
|
||||
}
|
||||
|
||||
|
||||
END
|
||||
|
||||
-- etags -r '/\([A-Za-z][-A-Za-z0-9]*\).*::=/\1/' k5.asn1
|
||||
|
@@ -13,6 +13,7 @@ AM_CPPFLAGS += \
|
||||
-I$(srcdir)/krb5 \
|
||||
-I$(srcdir)/spnego \
|
||||
-I$(srcdir)/sanon \
|
||||
-I$(srcdir)/preauth \
|
||||
$(INCLUDE_libintl)
|
||||
|
||||
lib_LTLIBRARIES = libgssapi.la test_negoex_mech.la
|
||||
@@ -250,12 +251,17 @@ sanonsrc = \
|
||||
sanon/release_name.c \
|
||||
sanon/sanon-private.h
|
||||
|
||||
preauthsrc = \
|
||||
preauth/pa_client.c \
|
||||
preauth/pa_common.c
|
||||
|
||||
dist_libgssapi_la_SOURCES = \
|
||||
$(krb5src) \
|
||||
$(mechsrc) \
|
||||
$(ntlmsrc) \
|
||||
$(spnegosrc) \
|
||||
$(sanonsrc)
|
||||
$(sanonsrc) \
|
||||
$(preauthsrc)
|
||||
|
||||
nodist_libgssapi_la_SOURCES = \
|
||||
gkrb5_err.c \
|
||||
@@ -289,6 +295,7 @@ noinst_HEADERS = \
|
||||
$(srcdir)/ntlm/ntlm-private.h \
|
||||
$(srcdir)/spnego/spnego-private.h \
|
||||
$(srcdir)/sanon/sanon-private.h \
|
||||
$(srcdir)/preauth/pa-private.h \
|
||||
$(srcdir)/krb5/gsskrb5-private.h
|
||||
|
||||
nobase_include_HEADERS = \
|
||||
@@ -296,6 +303,7 @@ nobase_include_HEADERS = \
|
||||
gssapi/gssapi_krb5.h \
|
||||
gssapi/gssapi_ntlm.h \
|
||||
gssapi/gssapi_oid.h \
|
||||
gssapi/gssapi_preauth.h \
|
||||
gssapi/gssapi_spnego.h
|
||||
|
||||
gssapidir = $(includedir)/gssapi
|
||||
@@ -319,7 +327,8 @@ BUILTHEADERS = \
|
||||
$(srcdir)/krb5/gsskrb5-private.h \
|
||||
$(srcdir)/spnego/spnego-private.h \
|
||||
$(srcdir)/sanon/sanon-private.h \
|
||||
$(srcdir)/ntlm/ntlm-private.h
|
||||
$(srcdir)/ntlm/ntlm-private.h \
|
||||
$(srcdir)/preauth/pa-private.h
|
||||
|
||||
$(libgssapi_la_OBJECTS): $(BUILTHEADERS)
|
||||
$(test_context_OBJECTS): $(BUILTHEADERS)
|
||||
@@ -356,6 +365,9 @@ $(srcdir)/spnego/spnego-private.h:
|
||||
$(srcdir)/sanon/sanon-private.h:
|
||||
cd $(srcdir) && perl ../../cf/make-proto.pl -q -P comment -p sanon/sanon-private.h $(sanonsrc) || rm -f sanon/sanon-private.h
|
||||
|
||||
$(srcdir)/preauth/pa-private.h:
|
||||
cd $(srcdir) && perl ../../cf/make-proto.pl -q -P comment -p preauth/pa-private.h $(preauthsrc) || rm -f preauth/pa-private.h
|
||||
|
||||
TESTS = test_oid test_names test_cfx
|
||||
# test_sequence
|
||||
|
||||
|
@@ -261,6 +261,10 @@ sanonsrc = \
|
||||
sanon/release_cred.c \
|
||||
sanon/release_name.c
|
||||
|
||||
preauthsrc = \
|
||||
preauth/pa_client.c \
|
||||
preauth/pa_common.c
|
||||
|
||||
$(OBJ)\ntlm\ntlm-private.h: $(ntlmsrc)
|
||||
$(PERL) ../../cf/make-proto.pl -q -P remove -p $@ $(ntlmsrc)
|
||||
|
||||
@@ -273,6 +277,9 @@ $(OBJ)\spnego\spnego-private.h: $(spnegosrc)
|
||||
$(OBJ)\sanon\sanon-private.h: $(sanonsrc)
|
||||
$(PERL) ../../cf/make-proto.pl -q -P remove -p $@ $(sanonsrc)
|
||||
|
||||
$(OBJ)\preauth\pa-private.h: $(preauthsrc)
|
||||
$(PERL) ../../cf/make-proto.pl -q -P remove -p $@ $(preauthsrc)
|
||||
|
||||
gssapi_files = $(OBJ)\gssapi\asn1_gssapi_asn1.x
|
||||
|
||||
spnego_files = $(OBJ)\spnego\asn1_spnego_asn1.x
|
||||
@@ -317,6 +324,7 @@ INCFILES= \
|
||||
$(OBJ)\ntlm\ntlm-private.h \
|
||||
$(OBJ)\spnego\spnego-private.h \
|
||||
$(OBJ)\sanon\sanon-private.h \
|
||||
$(OBJ)\preauth\pa-private.h \
|
||||
$(OBJ)\krb5\gsskrb5-private.h \
|
||||
$(OBJ)\gkrb5_err.h \
|
||||
$(OBJ)\negoex_err.h \
|
||||
@@ -533,6 +541,8 @@ libgssapi_OBJs = \
|
||||
$(OBJ)\sanon/process_context_token.obj \
|
||||
$(OBJ)\sanon/release_cred.obj \
|
||||
$(OBJ)\sanon/release_name.obj \
|
||||
$(OBJ)\preauth/pa_client.obj \
|
||||
$(OBJ)\preauth/pa_common.obj \
|
||||
$(OBJ)\gkrb5_err.obj \
|
||||
$(OBJ)\negoex_err.obj \
|
||||
$(spnego_files:.x=.obj) \
|
||||
@@ -570,6 +580,12 @@ GCOPTS=-I$(SRCDIR) -I$(OBJ) -Igssapi -DBUILD_GSSAPI_LIB
|
||||
{sanon}.c{$(OBJ)\sanon}.obj::
|
||||
$(C2OBJ_NP) -Fo$(OBJ)\sanon\ -Fd$(OBJ)\sanon\ -I$(OBJ)\sanon -I$(OBJ) -I$(OBJ)\krb5 -I$(OBJ)\gssapi -Ikrb5 -Imech -Igssapi $(GCOPTS) -DASN1_LIB
|
||||
|
||||
{$(OBJ)\preauth}.c{$(OBJ)\preauth}.obj::
|
||||
$(C2OBJ_NP) -Fo$(OBJ)\preauth\ -Fd$(OBJ)\pa\ -I$(OBJ)\pa -I$(OBJ) -I$(OBJ)\krb5 -I$(OBJ)\gssapi -Ikrb5 -Imech -Igssapi $(GCOPTS)
|
||||
|
||||
{preauth}.c{$(OBJ)\preauth}.obj::
|
||||
$(C2OBJ_NP) -Fo$(OBJ)\preauth\ -Fd$(OBJ)\pa\ -I$(OBJ)\pa -I$(OBJ) -I$(OBJ)\krb5 -I$(OBJ)\gssapi -Ikrb5 -Imech -Igssapi $(GCOPTS) -DASN1_LIB
|
||||
|
||||
{$(OBJ)\gssapi}.c{$(OBJ)\gssapi}.obj::
|
||||
$(C2OBJ_NP) -Fo$(OBJ)\gssapi\ -Fd$(OBJ)\gssapi\ -I$(OBJ)\gssapi $(GCOPTS)
|
||||
|
||||
@@ -660,6 +676,9 @@ mkdirs-gss:
|
||||
!if !exist($(OBJ)\gssapi)
|
||||
$(MKDIR) $(OBJ)\gssapi
|
||||
!endif
|
||||
!if !exist($(OBJ)\preauth)
|
||||
$(MKDIR) $(OBJ)\preauth
|
||||
!endif
|
||||
|
||||
clean::
|
||||
-$(RM) $(OBJ)\ntlm\*.*
|
||||
@@ -668,6 +687,7 @@ clean::
|
||||
-$(RM) $(OBJ)\mech\*.*
|
||||
-$(RM) $(OBJ)\sanon\*.*
|
||||
-$(RM) $(OBJ)\gssapi\*.*
|
||||
-$(RM) $(OBJ)\preauth\*.*
|
||||
|
||||
all-tools:: $(BINDIR)\gsstool.exe
|
||||
|
||||
|
@@ -1240,6 +1240,9 @@ GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
|
||||
gss_destroy_cred(OM_uint32 *minor_status,
|
||||
gss_cred_id_t *cred_handle);
|
||||
|
||||
GSSAPI_LIB_FUNCTION uintptr_t GSSAPI_CALLCONV
|
||||
gss_get_instance(const char *libname);
|
||||
|
||||
GSSAPI_CPP_END
|
||||
|
||||
#if defined(__APPLE__) && (defined(__ppc__) || defined(__ppc64__) || defined(__i386__) || defined(__x86_64__))
|
||||
|
55
lib/gssapi/gssapi/gssapi_preauth.h
Normal file
55
lib/gssapi/gssapi/gssapi_preauth.h
Normal file
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright (c) 1997 - 2006 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.
|
||||
*/
|
||||
|
||||
/* $Id$ */
|
||||
|
||||
#ifndef GSSAPI_PREAUTH_H_
|
||||
#define GSSAPI_PREAUTH_H_
|
||||
|
||||
#include <gssapi.h>
|
||||
#include <krb5.h>
|
||||
|
||||
GSSAPI_CPP_START
|
||||
|
||||
#if !defined(__GNUC__) && !defined(__attribute__)
|
||||
#define __attribute__(x)
|
||||
#endif
|
||||
|
||||
GSSAPI_LIB_FUNCTION krb5_error_code GSSAPI_LIB_CALL
|
||||
krb5_gss_set_init_creds(krb5_context context,
|
||||
krb5_init_creds_context ctx,
|
||||
gss_const_cred_id_t gss_cred,
|
||||
gss_const_OID gss_mech);
|
||||
|
||||
|
||||
#endif /* GSSAPI_PREAUTH_H_ */
|
@@ -40,6 +40,7 @@ EXPORTS
|
||||
gss_export_name
|
||||
gss_export_name_composite
|
||||
gss_export_sec_context
|
||||
gss_get_instance
|
||||
gss_get_mic
|
||||
gss_get_neg_mechs
|
||||
gss_get_name_attribute
|
||||
@@ -122,6 +123,14 @@ EXPORTS
|
||||
gsskrb5_set_send_to_kdc
|
||||
gsskrb5_set_time_offset
|
||||
krb5_gss_register_acceptor_identity
|
||||
krb5_gss_set_init_creds
|
||||
|
||||
_krb5_gss_data_to_buffer
|
||||
_krb5_gss_buffer_to_data
|
||||
_krb5_gss_map_error
|
||||
_krb5_gss_pa_parse_name
|
||||
_krb5_gss_pa_unparse_name
|
||||
_krb5_gss_pa_derive_key
|
||||
|
||||
; _gsskrb5cfx_ are really internal symbols, but export
|
||||
; then now to make testing easier.
|
||||
|
@@ -563,3 +563,16 @@ gss_oid_to_name(gss_const_OID oid)
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
GSSAPI_LIB_FUNCTION uintptr_t GSSAPI_CALLCONV
|
||||
gss_get_instance(const char *libname)
|
||||
{
|
||||
static const char *instance = "libgssapi";
|
||||
|
||||
if (strcmp(libname, "gssapi") == 0)
|
||||
return (uintptr_t)instance;
|
||||
else if (strcmp(libname, "krb5") == 0)
|
||||
return krb5_get_instance(libname);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
119
lib/gssapi/preauth/README.md
Normal file
119
lib/gssapi/preauth/README.md
Normal file
@@ -0,0 +1,119 @@
|
||||
# GSS-API Pre-authentication in Heimdal
|
||||
|
||||
GSS-API pre-authentication in Heimdal is based on
|
||||
[draft-perez-krb-wg-gss-preauth](https://datatracker.ietf.org/doc/html/draft-perez-krb-wg-gss-preauth)
|
||||
but with some simplifications to the protocol.
|
||||
|
||||
The following text assumes the reader is familiar with the draft.
|
||||
|
||||
## Protocol changes
|
||||
|
||||
- The pre-authentication type KRB5-PADATA-GSS is 655
|
||||
- Pre-authentication data is the raw context token rather than being
|
||||
wrapped in another ASN.1 type
|
||||
- Acceptor GSS state is stored in FX-COOKIE rather than alongside the
|
||||
context token
|
||||
- Key derivation salt is the string "KRB-GSS\0" || nonce
|
||||
|
||||
## Client side
|
||||
|
||||
Because libkrb5 cannot have a recursive dependency on libgssapi, it instead
|
||||
exports the function `_krb5_init_creds_init_gss()` which allows libgssapi to
|
||||
register a set of function pointers for:
|
||||
|
||||
- Generating context tokens
|
||||
- Finalizing a context (inquiring the initiator name and reply key)
|
||||
- Releasing context and credential handles
|
||||
|
||||
This is a private API.
|
||||
|
||||
This architecture also means that the libkrb5 implementation could be used with
|
||||
an alternative GSS-API implementation such as SSPI, without too much work. The
|
||||
bulk of the pre-authentication logic remains in libkrb5, however, in
|
||||
[`init_creds_pw.c`](../../krb5/init_creds_pw.c).
|
||||
|
||||
libgssapi itself exports `krb5_gss_set_init_creds()`, which is the public
|
||||
interface for GSS-API pre-authentication.
|
||||
|
||||
`krb5_gss_set_init_creds()` enables GSS-API pre-authentication on an initial
|
||||
credentials context, taking a GSS-API credential handle and mechanism. Both are
|
||||
optional; defaults will be used if absent. These two parameters are exposed as
|
||||
the `--gss-name` and `--gss-mech` options to `kinit` (see
|
||||
[kinit(1)](../../../kuser/kinit.1) for further details). `kinit` supports
|
||||
acquiring anonymous, keytab- and password-based GSS-API credentials using the
|
||||
same arguments as regular Kerberos.
|
||||
|
||||
The selected GSS-API mechanism must support mutual authentication (ie.
|
||||
authenticating the KDC) as it replaces the AS-REP reply key, However, if FAST
|
||||
was used, and we know that the KDC was verified, then this requirement is
|
||||
removed.
|
||||
|
||||
If the client does not know its initiator name, it can specify the last
|
||||
arugment to `kinit` as `@REALM`, and the initiator name will be filled in when
|
||||
the authentication is complete. (The realm is required to select a KDC.)
|
||||
|
||||
## KDC side
|
||||
|
||||
The KDC implements the acceptor side of the GSS-API authentication exchange.
|
||||
The selected GSS-API mechanism must allow `gss_export_sec_context()` to be
|
||||
called by the acceptor before the context is established, if it needs more than
|
||||
a single round trip of token exchanges.
|
||||
|
||||
### Configuration
|
||||
|
||||
Configuration directives live in the [kdc] section of
|
||||
[krb5.conf(5)](../../krb5/krb5.conf.5).
|
||||
|
||||
The `enable_gss_preauth` krb5.conf option must be set in order to enable
|
||||
GSS-API pre-authentication in the KDC. When authenticating federated principals
|
||||
which may not exist in the KDC, the `synthetic_clients` option should also be
|
||||
set.
|
||||
|
||||
The `gss_mechanisms_allowed` option can be used to limit the set of GSS-API
|
||||
mechanisms which are allowed to perform pre-authentication. Mechanisms are
|
||||
specified as dot-separated OIDs or by a short name, such as `sanon-x25519`.
|
||||
|
||||
The `enable_gss_auth_data` option will include a composite GSS name in the
|
||||
authorization data of returned tickets.
|
||||
|
||||
### Authorization
|
||||
|
||||
The default is that the initiator is permitted to authenticate to the Kerberos
|
||||
principal that directly corresponds to it. The correspondence is governed as
|
||||
follows: if the authenticating mechanism is in the list of mechanisms in the
|
||||
`gss_cross_realm_mechanisms_allowed` configuration option, then the principal
|
||||
is mapped identically: an initiator named `lukeh@AAA.PADL.COM` will be mapped
|
||||
to the Kerberos principal `lukeh@AAA.PADL.COM`.
|
||||
|
||||
If the authenticating mechanism is not in this list, then the initiator will be
|
||||
mapped to an enterprise principal in the service realm. For example,
|
||||
`lukeh@AAA.PADL.COM` might be mapped to `lukeh\@AAA.PADL.COM@PADL.COM`
|
||||
(enterprise principal name type);
|
||||
|
||||
This mapping has no effect for principals that exist in the HDB, because
|
||||
enterprise principal names are always looked up by their first component (as if
|
||||
they were an ordinary principal name). This logic is instead useful when
|
||||
synthetic principals are enabled as we wish to avoid issuing tickets with a
|
||||
client name in a foreign Kerberos realm, as that would conflate GSS-API
|
||||
"realms" with Kerberos realms.
|
||||
|
||||
A custom authorization plugin installed in `$prefix/lib/plugin/kdc` will
|
||||
replace this mapping and authorization logic. The plugin interface is defined in
|
||||
[`gss_preauth_authorizer_plugin.h`](../../../kdc/gss_preauth_authorizer_plugin.h)).
|
||||
|
||||
### Anonymous authentication
|
||||
|
||||
A further note on the interaction of anonymous GSS-API authentication and
|
||||
pre-authentication. Initiator contexts that set `GSS_C_ANON_FLAG` and a
|
||||
`GSS_C_NT_ANONYMOUS` name are mapped to the unauthenticated anonymous Kerberos
|
||||
principal, `WELLKNOWN/ANONYMOUS@WELLKNOWN:ANONYMOUS`. However, the local
|
||||
`WELLKNOWN/ANONYMOUS` HDB entry is used to perform any authorization decisions
|
||||
(as it would be for anonymous PKINIT). The AP-REP will contain the well-known
|
||||
anonymous realm.
|
||||
|
||||
If `GSS_C_NT_ANONYMOUS` was set but a different name type was returned, then
|
||||
the initiator is treated as authenticated anonymous, and the client realm will
|
||||
be present in the AP-REP.
|
||||
|
||||
The `request-anonymous` AP-REQ flag must also be set for GSS-API anonymous
|
||||
authentication to succeed.
|
251
lib/gssapi/preauth/pa_client.c
Normal file
251
lib/gssapi/preauth/pa_client.c
Normal file
@@ -0,0 +1,251 @@
|
||||
/*
|
||||
* Copyright (c) 2021, PADL Software Pty Ltd.
|
||||
* 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 PADL Software 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 PADL SOFTWARE 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 PADL SOFTWARE 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"
|
||||
#include "mech_locl.h"
|
||||
|
||||
#include <gssapi/gssapi_preauth.h>
|
||||
|
||||
#include <preauth/pa-private.h>
|
||||
|
||||
static krb5_error_code
|
||||
pa_gss_acquire_initiator_cred(krb5_context context,
|
||||
krb5_gss_init_ctx gssic,
|
||||
const krb5_creds *kcred,
|
||||
gss_cred_id_t *cred)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
|
||||
OM_uint32 major, minor;
|
||||
gss_const_OID mech;
|
||||
gss_OID_set_desc mechs;
|
||||
gss_name_t initiator_name = GSS_C_NO_NAME;
|
||||
OM_uint32 time_req;
|
||||
krb5_timestamp now;
|
||||
|
||||
*cred = GSS_C_NO_CREDENTIAL;
|
||||
|
||||
mech = _krb5_init_creds_get_gss_mechanism(context, gssic);
|
||||
|
||||
mechs.count = 1;
|
||||
mechs.elements = (gss_OID)mech;
|
||||
|
||||
ret = _krb5_gss_pa_unparse_name(context, kcred->client, &initiator_name);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
krb5_timeofday(context, &now);
|
||||
if (kcred->times.endtime && kcred->times.endtime > now)
|
||||
time_req = kcred->times.endtime - now;
|
||||
else
|
||||
time_req = GSS_C_INDEFINITE;
|
||||
|
||||
major = gss_acquire_cred(&minor, initiator_name, time_req, &mechs,
|
||||
GSS_C_INITIATE, cred, NULL, NULL);
|
||||
ret = _krb5_gss_map_error(major, minor);
|
||||
|
||||
gss_release_name(&major, &initiator_name);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static krb5_error_code KRB5_LIB_CALL
|
||||
pa_gss_step(krb5_context context,
|
||||
krb5_gss_init_ctx gssic,
|
||||
const krb5_creds *kcred,
|
||||
KDCOptions flags,
|
||||
krb5_data *enc_as_req,
|
||||
krb5_data *in,
|
||||
krb5_data *out)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
|
||||
OM_uint32 major, minor;
|
||||
gss_cred_id_t cred;
|
||||
gss_ctx_id_t ctx;
|
||||
gss_name_t target_name = GSS_C_NO_NAME;
|
||||
OM_uint32 req_flags = GSS_C_MUTUAL_FLAG;
|
||||
OM_uint32 ret_flags;
|
||||
struct gss_channel_bindings_struct cb = { 0 };
|
||||
gss_buffer_desc input_token, output_token = GSS_C_EMPTY_BUFFER;
|
||||
|
||||
krb5_data_zero(out);
|
||||
|
||||
if (flags.request_anonymous)
|
||||
req_flags |= GSS_C_ANON_FLAG;
|
||||
|
||||
cred = (gss_cred_id_t)_krb5_init_creds_get_gss_cred(context, gssic);
|
||||
|
||||
if (cred == GSS_C_NO_CREDENTIAL) {
|
||||
ret = pa_gss_acquire_initiator_cred(context, gssic, kcred, &cred);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
_krb5_init_creds_set_gss_cred(context, gssic, cred);
|
||||
}
|
||||
|
||||
ctx = (gss_ctx_id_t)_krb5_init_creds_get_gss_context(context, gssic);
|
||||
|
||||
ret = _krb5_gss_pa_unparse_name(context, kcred->server, &target_name);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
_krb5_gss_data_to_buffer(enc_as_req, &cb.application_data);
|
||||
_krb5_gss_data_to_buffer(in, &input_token);
|
||||
|
||||
major = gss_init_sec_context(&minor,
|
||||
cred,
|
||||
&ctx,
|
||||
target_name,
|
||||
(gss_OID)_krb5_init_creds_get_gss_mechanism(context, gssic),
|
||||
req_flags,
|
||||
GSS_C_INDEFINITE,
|
||||
&cb,
|
||||
&input_token,
|
||||
NULL,
|
||||
&output_token,
|
||||
&ret_flags,
|
||||
NULL);
|
||||
|
||||
_krb5_init_creds_set_gss_context(context, gssic, ctx);
|
||||
|
||||
_krb5_gss_buffer_to_data(&output_token, out);
|
||||
_mg_buffer_zero(&output_token);
|
||||
|
||||
if (major == GSS_S_COMPLETE) {
|
||||
if ((ret_flags & GSS_C_MUTUAL_FLAG) == 0)
|
||||
ret = KRB5_MUTUAL_FAILED;
|
||||
else if ((ret_flags & req_flags) != req_flags)
|
||||
ret = KRB5KDC_ERR_BADOPTION;
|
||||
else
|
||||
ret = 0;
|
||||
} else
|
||||
ret = _krb5_gss_map_error(major, minor);
|
||||
|
||||
out:
|
||||
gss_release_name(&minor, &target_name);
|
||||
gss_release_buffer(&minor, &output_token);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static krb5_error_code KRB5_LIB_CALL
|
||||
pa_gss_finish(krb5_context context,
|
||||
krb5_gss_init_ctx gssic,
|
||||
const krb5_creds *kcred,
|
||||
krb5int32 nonce,
|
||||
krb5_enctype enctype,
|
||||
krb5_principal *client_p,
|
||||
krb5_keyblock **reply_key_p)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
krb5_principal client = NULL;
|
||||
krb5_keyblock *reply_key = NULL;
|
||||
|
||||
OM_uint32 major, minor;
|
||||
gss_name_t initiator_name = GSS_C_NO_NAME;
|
||||
gss_ctx_id_t ctx = (gss_ctx_id_t)_krb5_init_creds_get_gss_context(context, gssic);
|
||||
|
||||
*client_p = NULL;
|
||||
*reply_key_p = NULL;
|
||||
|
||||
major = gss_inquire_context(&minor,
|
||||
ctx,
|
||||
&initiator_name,
|
||||
NULL, /* target_name */
|
||||
NULL, /* lifetime_req */
|
||||
NULL, /* mech_type */
|
||||
NULL, /* ctx_flags */
|
||||
NULL, /* locally_initiated */
|
||||
NULL); /* open */
|
||||
|
||||
if (GSS_ERROR(major))
|
||||
return _krb5_gss_map_error(major, minor);
|
||||
|
||||
ret = _krb5_gss_pa_parse_name(context, initiator_name, 0, &client);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = _krb5_gss_pa_derive_key(context, ctx, nonce, enctype, &reply_key);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
*client_p = client;
|
||||
client = NULL;
|
||||
|
||||
*reply_key_p = reply_key;
|
||||
reply_key = NULL;
|
||||
|
||||
out:
|
||||
krb5_free_principal(context, client);
|
||||
if (reply_key)
|
||||
krb5_free_keyblock(context, reply_key);
|
||||
gss_release_name(&minor, &initiator_name);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void KRB5_LIB_CALL
|
||||
pa_gss_delete_sec_context(krb5_context context,
|
||||
krb5_gss_init_ctx gssic,
|
||||
gss_ctx_id_t ctx)
|
||||
{
|
||||
OM_uint32 minor;
|
||||
|
||||
gss_delete_sec_context(&minor, &ctx, GSS_C_NO_BUFFER);
|
||||
}
|
||||
|
||||
static void KRB5_LIB_CALL
|
||||
pa_gss_release_cred(krb5_context context,
|
||||
krb5_gss_init_ctx gssic,
|
||||
gss_cred_id_t cred)
|
||||
{
|
||||
OM_uint32 minor;
|
||||
|
||||
gss_release_cred(&minor, &cred);
|
||||
}
|
||||
|
||||
GSSAPI_LIB_FUNCTION krb5_error_code GSSAPI_LIB_CALL
|
||||
krb5_gss_set_init_creds(krb5_context context,
|
||||
krb5_init_creds_context ctx,
|
||||
gss_const_cred_id_t gss_cred,
|
||||
gss_const_OID gss_mech)
|
||||
{
|
||||
return _krb5_init_creds_init_gss(context,ctx,
|
||||
pa_gss_step,
|
||||
pa_gss_finish,
|
||||
pa_gss_release_cred,
|
||||
pa_gss_delete_sec_context,
|
||||
gss_cred,
|
||||
gss_mech,
|
||||
0);
|
||||
}
|
215
lib/gssapi/preauth/pa_common.c
Normal file
215
lib/gssapi/preauth/pa_common.c
Normal file
@@ -0,0 +1,215 @@
|
||||
/*
|
||||
* Copyright (c) 2021, PADL Software Pty Ltd.
|
||||
* 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 PADL Software 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 PADL SOFTWARE 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 PADL SOFTWARE 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"
|
||||
#include "mech_locl.h"
|
||||
|
||||
#include <gssapi/gssapi_preauth.h>
|
||||
|
||||
#include <preauth/pa-private.h>
|
||||
|
||||
krb5_error_code
|
||||
_krb5_gss_map_error(OM_uint32 major, OM_uint32 minor)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
|
||||
if (minor != 0)
|
||||
return (krb5_error_code)minor;
|
||||
|
||||
switch (major) {
|
||||
case GSS_S_COMPLETE:
|
||||
ret = 0;
|
||||
break;
|
||||
case GSS_S_CONTINUE_NEEDED:
|
||||
ret = KRB5_KDC_ERR_MORE_PREAUTH_DATA_REQUIRED;
|
||||
break;
|
||||
case GSS_S_BAD_NAME:
|
||||
case GSS_S_BAD_NAMETYPE:
|
||||
ret = KRB5_PRINC_NOMATCH;
|
||||
break;
|
||||
case GSS_S_BAD_MIC:
|
||||
ret = KRB5KRB_AP_ERR_BAD_INTEGRITY;
|
||||
break;
|
||||
case GSS_S_FAILURE:
|
||||
default:
|
||||
ret = KRB5KDC_ERR_PREAUTH_FAILED;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
krb5_error_code
|
||||
_krb5_gss_pa_derive_key(krb5_context context,
|
||||
gss_ctx_id_t ctx,
|
||||
krb5int32 nonce,
|
||||
krb5_enctype enctype,
|
||||
krb5_keyblock **keyblock)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
u_char saltdata[12] = "KRB-GSS";
|
||||
krb5_keyblock kdkey;
|
||||
size_t keysize;
|
||||
|
||||
OM_uint32 major, minor;
|
||||
gss_buffer_desc salt, dkey = GSS_C_EMPTY_BUFFER;
|
||||
|
||||
*keyblock = NULL;
|
||||
|
||||
ret = krb5_enctype_keysize(context, enctype, &keysize);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
_gss_mg_encode_le_uint32(nonce, &saltdata[8]);
|
||||
|
||||
salt.value = saltdata;
|
||||
salt.length = sizeof(saltdata);
|
||||
|
||||
major = gss_pseudo_random(&minor, ctx, GSS_C_PRF_KEY_FULL,
|
||||
&salt, keysize, &dkey);
|
||||
if (GSS_ERROR(major))
|
||||
return KRB5_PREAUTH_NO_KEY;
|
||||
|
||||
kdkey.keytype = enctype;
|
||||
kdkey.keyvalue.data = dkey.value;
|
||||
kdkey.keyvalue.length = dkey.length;
|
||||
|
||||
ret = krb5_copy_keyblock(context, &kdkey, keyblock);
|
||||
|
||||
_gss_secure_release_buffer(&minor, &dkey);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
krb5_error_code
|
||||
_krb5_gss_pa_unparse_name(krb5_context context,
|
||||
krb5_const_principal principal,
|
||||
gss_name_t *namep)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
char *name = NULL;
|
||||
|
||||
OM_uint32 major, minor;
|
||||
gss_buffer_desc name_buf;
|
||||
gss_OID name_type;
|
||||
|
||||
*namep = GSS_C_NO_NAME;
|
||||
|
||||
if (principal->name.name_type == KRB5_NT_ENTERPRISE_PRINCIPAL) {
|
||||
if (principal->name.name_string.len != 1)
|
||||
return EINVAL;
|
||||
|
||||
name = principal->name.name_string.val[0];
|
||||
} else {
|
||||
ret = krb5_unparse_name(context, principal, &name);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
name_buf.length = strlen(name);
|
||||
name_buf.value = name;
|
||||
|
||||
if (principal->name.name_type == KRB5_NT_PRINCIPAL ||
|
||||
principal->name.name_type == KRB5_NT_ENTERPRISE_PRINCIPAL)
|
||||
name_type = GSS_C_NT_USER_NAME;
|
||||
else
|
||||
name_type = GSS_KRB5_NT_PRINCIPAL_NAME;
|
||||
|
||||
major = gss_import_name(&minor, &name_buf, name_type, namep);
|
||||
|
||||
if (name != principal->name.name_string.val[0])
|
||||
krb5_xfree(name);
|
||||
|
||||
return _krb5_gss_map_error(major, minor);
|
||||
}
|
||||
|
||||
krb5_error_code
|
||||
_krb5_gss_pa_parse_name(krb5_context context,
|
||||
gss_const_name_t name,
|
||||
int flags,
|
||||
krb5_principal *principal)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
char *displayed_name0;
|
||||
|
||||
OM_uint32 major, minor;
|
||||
gss_OID name_type = GSS_C_NO_OID;
|
||||
gss_buffer_desc displayed_name = GSS_C_EMPTY_BUFFER;
|
||||
|
||||
major = gss_display_name(&minor, name, &displayed_name, &name_type);
|
||||
if (GSS_ERROR(major))
|
||||
return _krb5_gss_map_error(major, minor);
|
||||
|
||||
if (gss_oid_equal(name_type, GSS_C_NT_ANONYMOUS)) {
|
||||
ret = krb5_make_principal(context, principal, KRB5_ANON_REALM,
|
||||
KRB5_WELLKNOWN_NAME, KRB5_ANON_NAME, NULL);
|
||||
if (ret == 0)
|
||||
(*principal)->name.name_type = KRB5_NT_WELLKNOWN;
|
||||
} else {
|
||||
displayed_name0 = malloc(displayed_name.length + 1);
|
||||
if (displayed_name0 == NULL)
|
||||
return krb5_enomem(context);
|
||||
|
||||
memcpy(displayed_name0, displayed_name.value, displayed_name.length);
|
||||
displayed_name0[displayed_name.length] = '\0';
|
||||
|
||||
ret = krb5_parse_name_flags(context, displayed_name0, flags, principal);
|
||||
gss_release_buffer(&minor, &displayed_name);
|
||||
free(displayed_name0);
|
||||
}
|
||||
|
||||
gss_release_buffer(&minor, &displayed_name);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void
|
||||
_krb5_gss_data_to_buffer(const krb5_data *data, gss_buffer_t buffer)
|
||||
{
|
||||
if (data) {
|
||||
buffer->length = data->length;
|
||||
buffer->value = data->data;
|
||||
} else {
|
||||
_mg_buffer_zero(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
_krb5_gss_buffer_to_data(gss_const_buffer_t buffer, krb5_data *data)
|
||||
{
|
||||
if (buffer) {
|
||||
data->length = buffer->length;
|
||||
data->data = buffer->value;
|
||||
} else {
|
||||
krb5_data_zero(data);
|
||||
}
|
||||
}
|
@@ -43,6 +43,7 @@ HEIMDAL_GSS_2.0 {
|
||||
gss_export_name;
|
||||
gss_export_name_composite;
|
||||
gss_export_sec_context;
|
||||
gss_get_instance;
|
||||
gss_get_mic;
|
||||
gss_get_neg_mechs;
|
||||
gss_get_name_attribute;
|
||||
@@ -116,6 +117,7 @@ HEIMDAL_GSS_2.0 {
|
||||
gsskrb5_set_send_to_kdc;
|
||||
gsskrb5_set_time_offset;
|
||||
krb5_gss_register_acceptor_identity;
|
||||
krb5_gss_set_init_creds;
|
||||
gss_display_mech_attr;
|
||||
gss_inquire_attrs_for_mech;
|
||||
gss_indicate_mechs_by_attrs;
|
||||
@@ -133,6 +135,13 @@ HEIMDAL_GSS_2.0 {
|
||||
_gsskrb5cfx_wrap_length_cfx;
|
||||
_gssapi_wrap_size_cfx;
|
||||
|
||||
_krb5_gss_data_to_buffer;
|
||||
_krb5_gss_buffer_to_data;
|
||||
_krb5_gss_map_error;
|
||||
_krb5_gss_pa_parse_name;
|
||||
_krb5_gss_pa_unparse_name;
|
||||
_krb5_gss_pa_derive_key;
|
||||
|
||||
__gss_krb5_copy_ccache_x_oid_desc;
|
||||
__gss_krb5_get_tkt_flags_x_oid_desc;
|
||||
__gss_krb5_extract_authz_data_from_sec_context_x_oid_desc;
|
||||
|
@@ -38,6 +38,21 @@
|
||||
#include <heim-ipc.h>
|
||||
#endif /* WIN32 */
|
||||
|
||||
struct krb5_gss_init_ctx_data {
|
||||
unsigned int flags;
|
||||
#define GSSIC_FLAG_RELEASE_CRED (1 << 0)
|
||||
#define GSSIC_FLAG_CONTEXT_OPEN (1 << 1)
|
||||
|
||||
krb5_gssic_step step;
|
||||
krb5_gssic_finish finish;
|
||||
krb5_gssic_release_cred release_cred;
|
||||
krb5_gssic_delete_sec_context delete_sec_context;
|
||||
|
||||
const struct gss_OID_desc_struct *mech;
|
||||
struct gss_cred_id_t_desc_struct *cred;
|
||||
struct gss_ctx_id_t_desc_struct *ctx;
|
||||
};
|
||||
|
||||
typedef struct krb5_get_init_creds_ctx {
|
||||
KDCOptions flags;
|
||||
krb5_creds cred;
|
||||
@@ -62,6 +77,7 @@ typedef struct krb5_get_init_creds_ctx {
|
||||
krb5_get_init_creds_tristate req_pac;
|
||||
|
||||
krb5_pk_init_ctx pk_init_ctx;
|
||||
krb5_gss_init_ctx gss_init_ctx;
|
||||
int ic_flags;
|
||||
|
||||
struct {
|
||||
@@ -106,6 +122,7 @@ typedef struct krb5_get_init_creds_ctx {
|
||||
krb5_keyblock *strengthen_key;
|
||||
krb5_get_init_creds_opt *anon_pkinit_opt;
|
||||
krb5_init_creds_context anon_pkinit_ctx;
|
||||
krb5_data cookie;
|
||||
} fast_state;
|
||||
} krb5_get_init_creds_ctx;
|
||||
|
||||
@@ -155,6 +172,15 @@ default_s2k_func(krb5_context context, krb5_enctype type,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
free_gss_init_ctx(krb5_context context, krb5_gss_init_ctx gssic)
|
||||
{
|
||||
if (gssic->flags & GSSIC_FLAG_RELEASE_CRED)
|
||||
gssic->release_cred(context, gssic, gssic->cred);
|
||||
gssic->delete_sec_context(context, gssic, gssic->ctx);
|
||||
free(gssic);
|
||||
}
|
||||
|
||||
static void
|
||||
free_init_creds_ctx(krb5_context context, krb5_init_creds_context ctx)
|
||||
{
|
||||
@@ -172,6 +198,8 @@ free_init_creds_ctx(krb5_context context, krb5_init_creds_context ctx)
|
||||
memset_s(ctx->password, len, 0, len);
|
||||
free(ctx->password);
|
||||
}
|
||||
if (ctx->gss_init_ctx)
|
||||
free_gss_init_ctx(context, ctx->gss_init_ctx);
|
||||
/*
|
||||
* FAST state (we don't close the armor_ccache because we might have
|
||||
* to destroy it, and how would we know? also, the caller should
|
||||
@@ -183,6 +211,7 @@ free_init_creds_ctx(krb5_context context, krb5_init_creds_context ctx)
|
||||
krb5_crypto_destroy(context, ctx->fast_state.armor_crypto);
|
||||
if (ctx->fast_state.strengthen_key)
|
||||
krb5_free_keyblock(context, ctx->fast_state.strengthen_key);
|
||||
krb5_data_free(&ctx->fast_state.cookie);
|
||||
krb5_free_keyblock_contents(context, &ctx->fast_state.armor_key);
|
||||
if (ctx->fast_state.flags & KRB5_FAST_ANON_PKINIT_ARMOR)
|
||||
krb5_cc_destroy(context, ctx->fast_state.armor_ccache);
|
||||
@@ -1169,6 +1198,105 @@ pa_data_to_md_pkinit(krb5_context context,
|
||||
#endif
|
||||
}
|
||||
|
||||
static krb5_error_code
|
||||
gss_pa_step(krb5_context context,
|
||||
const krb5_creds *creds,
|
||||
const krb5_get_init_creds_ctx *ctx,
|
||||
const METHOD_DATA *md,
|
||||
krb5_data *output_token)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
size_t len = 0;
|
||||
krb5_gss_init_ctx gssic = ctx->gss_init_ctx;
|
||||
krb5_data req_body;
|
||||
PA_DATA *pa;
|
||||
krb5_data *input_token;
|
||||
|
||||
krb5_data_zero(&req_body);
|
||||
krb5_data_zero(output_token);
|
||||
|
||||
pa = find_pa_data(md, KRB5_PADATA_GSS);
|
||||
input_token = pa ? &pa->padata_value : NULL;
|
||||
|
||||
if (input_token == NULL || input_token->length == 0) {
|
||||
if (gssic->ctx == NULL)
|
||||
ret = 0; /* initial context token */
|
||||
else {
|
||||
krb5_set_error_message(context, KRB5_PREAUTH_BAD_TYPE,
|
||||
"Missing GSS preauthentication data from KDC");
|
||||
ret = KRB5_PREAUTH_BAD_TYPE;
|
||||
}
|
||||
if (ret)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (input_token && input_token->length &&
|
||||
(gssic->flags & GSSIC_FLAG_CONTEXT_OPEN)) {
|
||||
krb5_set_error_message(context, KRB5_GET_IN_TKT_LOOP,
|
||||
"Already completed GSS authentication, looping");
|
||||
ret = KRB5_GET_IN_TKT_LOOP;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ASN1_MALLOC_ENCODE(KDC_REQ_BODY, req_body.data, req_body.length,
|
||||
&ctx->as_req.req_body, &len, ret);
|
||||
if (ret)
|
||||
goto out;
|
||||
heim_assert(req_body.length == len, "ASN.1 internal error");
|
||||
|
||||
ret = gssic->step(context, gssic, creds, ctx->flags, &req_body,
|
||||
input_token, output_token);
|
||||
|
||||
/*
|
||||
* If FAST authenticated the KDC (which will be the case unless anonymous
|
||||
* PKINIT was used without KDC certificate validation) then we can relax
|
||||
* the mutual authentication requirement.
|
||||
*/
|
||||
if (ret == KRB5_MUTUAL_FAILED &&
|
||||
(ctx->fast_state.flags & KRB5_FAST_EXPECTED) &&
|
||||
(ctx->fast_state.flags & KRB5_FAST_KDC_VERIFIED))
|
||||
ret = 0;
|
||||
|
||||
out:
|
||||
krb5_data_free(&req_body);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static krb5_error_code
|
||||
pa_data_to_md_gss(krb5_context context,
|
||||
const AS_REQ *a,
|
||||
const krb5_creds *creds,
|
||||
krb5_get_init_creds_ctx *ctx,
|
||||
METHOD_DATA *in_md,
|
||||
METHOD_DATA *out_md)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
krb5_data output_token;
|
||||
krb5_gss_init_ctx gssic = ctx->gss_init_ctx;
|
||||
|
||||
heim_assert(gssic != NULL, "invalid context passed to pa_data_to_md_gss");
|
||||
|
||||
ret = gss_pa_step(context, creds, ctx, in_md, &output_token);
|
||||
if (ret == 0)
|
||||
gssic->flags |= GSSIC_FLAG_CONTEXT_OPEN;
|
||||
else if (ret == KRB5_KDC_ERR_MORE_PREAUTH_DATA_REQUIRED)
|
||||
ret = 0;
|
||||
|
||||
if (output_token.length) {
|
||||
ret = krb5_padata_add(context, out_md, KRB5_PADATA_GSS,
|
||||
output_token.data, output_token.length);
|
||||
if (ret) {
|
||||
krb5_data_free(&output_token);
|
||||
return ret;
|
||||
}
|
||||
krb5_data_zero(&output_token);
|
||||
} else
|
||||
krb5_data_free(&output_token);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static krb5_error_code
|
||||
pa_data_add_pac_request(krb5_context context,
|
||||
krb5_get_init_creds_ctx *ctx,
|
||||
@@ -1261,6 +1389,12 @@ process_pa_data_to_md(krb5_context context,
|
||||
else
|
||||
ctx->used_pa_types |= USED_PKINIT;
|
||||
|
||||
} else if (ctx->gss_init_ctx) {
|
||||
_krb5_debug(context, 5, "krb5_get_init_creds: preparing GSS credentials");
|
||||
|
||||
ret = pa_data_to_md_gss(context, a, creds, ctx, in_md, *out_md);
|
||||
if (ret)
|
||||
return ret;
|
||||
} else if (in_md->len != 0) {
|
||||
struct pa_info_data *paid, *ppaid;
|
||||
unsigned flag;
|
||||
@@ -1317,6 +1451,61 @@ process_pa_data_to_md(krb5_context context,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static krb5_error_code
|
||||
gss_pa_data_to_key(krb5_context context,
|
||||
krb5_get_init_creds_ctx *ctx,
|
||||
krb5_creds *creds,
|
||||
krb5_enctype etype,
|
||||
METHOD_DATA *md,
|
||||
krb5_keyblock **key)
|
||||
{
|
||||
krb5_error_code ret;
|
||||
krb5_principal cname = NULL;
|
||||
krb5_gss_init_ctx gssic = ctx->gss_init_ctx;
|
||||
|
||||
/*
|
||||
* Finish the GSS authentication and extract the reply key. If the GSS mechanism
|
||||
* required an odd number of legs, the context may already be open, but we still
|
||||
* needed to wait for a reply from the KDC.
|
||||
*/
|
||||
if ((gssic->flags & GSSIC_FLAG_CONTEXT_OPEN) == 0) {
|
||||
krb5_data output_token;
|
||||
|
||||
ret = gss_pa_step(context, creds, ctx, md, &output_token);
|
||||
if (ret == KRB5_KDC_ERR_MORE_PREAUTH_DATA_REQUIRED ||
|
||||
output_token.length) {
|
||||
krb5_set_error_message(context, KRB5KDC_ERR_PREAUTH_FAILED,
|
||||
"KDC sent AS-REP before GSS preauthentication completed");
|
||||
ret = KRB5_PREAUTH_FAILED;
|
||||
}
|
||||
krb5_data_free(&output_token);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
gssic->flags |= GSSIC_FLAG_CONTEXT_OPEN;
|
||||
}
|
||||
|
||||
ret = gssic->finish(context, gssic, creds, ctx->nonce, etype, &cname, key);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
if (krb5_principal_is_federated(context, creds->client)) {
|
||||
/* replace the wellknown federated name with the initiator name */
|
||||
krb5_free_principal(context, creds->client);
|
||||
creds->client = cname;
|
||||
cname = NULL;
|
||||
|
||||
/* allow the KDC to canonicalize the name */
|
||||
if (ctx->flags.canonicalize)
|
||||
ctx->ic_flags |= KRB5_INIT_CREDS_NO_C_CANON_CHECK;
|
||||
}
|
||||
|
||||
out:
|
||||
krb5_free_principal(context, cname);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static krb5_error_code
|
||||
process_pa_data_to_key(krb5_context context,
|
||||
krb5_get_init_creds_ctx *ctx,
|
||||
@@ -1353,18 +1542,9 @@ process_pa_data_to_key(krb5_context context,
|
||||
|
||||
pa = NULL;
|
||||
if (rep->padata) {
|
||||
int idx = 0;
|
||||
pa = krb5_find_padata(rep->padata->val,
|
||||
rep->padata->len,
|
||||
KRB5_PADATA_PK_AS_REP,
|
||||
&idx);
|
||||
if (pa == NULL) {
|
||||
idx = 0;
|
||||
pa = krb5_find_padata(rep->padata->val,
|
||||
rep->padata->len,
|
||||
KRB5_PADATA_PK_AS_REP_19,
|
||||
&idx);
|
||||
}
|
||||
pa = find_pa_data(rep->padata, KRB5_PADATA_PK_AS_REP);
|
||||
if (pa == NULL)
|
||||
pa = find_pa_data(rep->padata, KRB5_PADATA_PK_AS_REP_19);
|
||||
}
|
||||
if (pa && ctx->pk_init_ctx) {
|
||||
#ifdef PKINIT
|
||||
@@ -1383,6 +1563,9 @@ process_pa_data_to_key(krb5_context context,
|
||||
ret = EINVAL;
|
||||
krb5_set_error_message(context, ret, N_("no support for PKINIT compiled in", ""));
|
||||
#endif
|
||||
} else if (ctx->gss_init_ctx) {
|
||||
_krb5_debug(context, 5, "krb5_get_init_creds: using GSS for reply key");
|
||||
ret = gss_pa_data_to_key(context, ctx, creds, etype, rep->padata, key);
|
||||
} else if (ctx->keyseed) {
|
||||
_krb5_debug(context, 5, "krb5_get_init_creds: using keyproc");
|
||||
ret = pa_data_to_key_plain(context, creds->client, ctx,
|
||||
@@ -1765,15 +1948,12 @@ fast_unwrap_as_rep(krb5_context context, int32_t nonce,
|
||||
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);
|
||||
pa = find_pa_data(rep->padata, KRB5_PADATA_FX_FAST);
|
||||
if (pa == NULL)
|
||||
return check_fast(context, state);
|
||||
|
||||
@@ -1863,17 +2043,105 @@ fast_unwrap_as_rep(krb5_context context, int32_t nonce,
|
||||
|
||||
out:
|
||||
free_PA_FX_FAST_REPLY(&fxfastrep);
|
||||
free_KrbFastResponse(&fastrep);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static krb5_error_code
|
||||
fast_unwrap_error(krb5_context context, struct fast_state *state, KRB_ERROR *error)
|
||||
fast_unwrap_error(krb5_context context,
|
||||
int32_t nonce,
|
||||
struct fast_state *state,
|
||||
KRB_ERROR *error,
|
||||
METHOD_DATA *error_method)
|
||||
{
|
||||
if (state->armor_crypto == NULL)
|
||||
return check_fast(context, state);
|
||||
METHOD_DATA md;
|
||||
PA_FX_FAST_REPLY fxfastrep;
|
||||
KrbFastResponse fastrep;
|
||||
krb5_error_code ret;
|
||||
PA_DATA *pa;
|
||||
KRB_ERROR fast_error;
|
||||
|
||||
return 0;
|
||||
memset(&md, 0, sizeof(md));
|
||||
memset(&fxfastrep, 0, sizeof(fxfastrep));
|
||||
memset(&fastrep, 0, sizeof(fastrep));
|
||||
memset(&fast_error, 0, sizeof(fast_error));
|
||||
|
||||
if (error->e_data) {
|
||||
ret = decode_METHOD_DATA(error->e_data->data,
|
||||
error->e_data->length, &md, NULL);
|
||||
if (ret) {
|
||||
krb5_set_error_message(context, ret,
|
||||
N_("Failed to decode METHOD-DATA", ""));
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
if (state->armor_crypto == NULL ||
|
||||
(pa = find_pa_data(&md, KRB5_PADATA_FX_FAST)) == NULL) {
|
||||
free_METHOD_DATA(error_method);
|
||||
*error_method = md;
|
||||
return check_fast(context, state);
|
||||
}
|
||||
|
||||
ret = decode_PA_FX_FAST_REPLY(pa->padata_value.data,
|
||||
pa->padata_value.length,
|
||||
&fxfastrep,
|
||||
NULL);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/* RFC 6113 5.4.3: strengthen key must be absent in error reply */
|
||||
if (fastrep.strengthen_key || nonce != fastrep.nonce) {
|
||||
ret = KRB5KDC_ERR_PREAUTH_FAILED;
|
||||
goto out;
|
||||
}
|
||||
|
||||
pa = find_pa_data(&fastrep.padata, KRB5_PADATA_FX_ERROR);
|
||||
if (pa == NULL) {
|
||||
ret = KRB5KDC_ERR_PREAUTH_FAILED;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = krb5_rd_error(context, &pa->padata_value, &fast_error);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
free_KRB_ERROR(error);
|
||||
*error = fast_error;
|
||||
memset(&fast_error, 0, sizeof(fast_error));
|
||||
|
||||
free_METHOD_DATA(error_method);
|
||||
*error_method = fastrep.padata;
|
||||
memset(&fastrep.padata, 0, sizeof(fastrep.padata));
|
||||
|
||||
out:
|
||||
free_METHOD_DATA(&md);
|
||||
free_PA_FX_FAST_REPLY(&fxfastrep);
|
||||
free_KrbFastResponse(&fastrep);
|
||||
free_KRB_ERROR(&fast_error);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
krb5_error_code
|
||||
@@ -2058,7 +2326,9 @@ make_fast_ap_fxarmor(krb5_context context,
|
||||
}
|
||||
|
||||
static krb5_error_code
|
||||
fast_wrap_req(krb5_context context, struct fast_state *state, KDC_REQ *req)
|
||||
fast_wrap_req(krb5_context context,
|
||||
struct fast_state *state,
|
||||
KDC_REQ *req)
|
||||
{
|
||||
KrbFastArmor *fxarmor = NULL;
|
||||
PA_FX_FAST_REQUEST fxreq;
|
||||
@@ -2067,6 +2337,16 @@ fast_wrap_req(krb5_context context, struct fast_state *state, KDC_REQ *req)
|
||||
krb5_data data;
|
||||
size_t size;
|
||||
|
||||
/* FX-COOKIE can be used without FAST armor */
|
||||
if (state->cookie.data) {
|
||||
ret = krb5_padata_add(context, req->padata, KRB5_PADATA_FX_COOKIE,
|
||||
state->cookie.data, state->cookie.length);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
krb5_data_zero(&state->cookie);
|
||||
}
|
||||
|
||||
if (state->flags & KRB5_FAST_DISABLED) {
|
||||
_krb5_debug(context, 10, "fast disabled, not doing any fast wrapping");
|
||||
return 0;
|
||||
@@ -2196,6 +2476,43 @@ fast_wrap_req(krb5_context context, struct fast_state *state, KDC_REQ *req)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static krb5_error_code
|
||||
fast_validate_conversation(METHOD_DATA *md, struct fast_state *state)
|
||||
{
|
||||
PA_DATA *pa;
|
||||
|
||||
/*
|
||||
* RFC 6113 5.4.3: PA-FX-COOKIE MUST be included if the KDC
|
||||
* expects at least one more message from the client.
|
||||
*/
|
||||
pa = find_pa_data(md, KRB5_PADATA_FX_COOKIE);
|
||||
if (pa == NULL)
|
||||
return KRB5_PREAUTH_FAILED;
|
||||
|
||||
krb5_data_free(&state->cookie);
|
||||
state->cookie = pa->padata_value;
|
||||
krb5_data_zero(&pa->padata_value);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static size_t
|
||||
available_padata_count(METHOD_DATA *md)
|
||||
{
|
||||
size_t i, count = 0;
|
||||
|
||||
for (i = 0; i < md->len; i++) {
|
||||
PA_DATA *pa = &md->val[i];
|
||||
|
||||
if (pa->padata_type == KRB5_PADATA_FX_COOKIE ||
|
||||
pa->padata_type == KRB5_PADATA_FX_ERROR)
|
||||
continue;
|
||||
|
||||
count++;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static krb5_error_code
|
||||
init_creds_step(krb5_context context,
|
||||
@@ -2219,7 +2536,7 @@ init_creds_step(krb5_context context,
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define MAX_PA_COUNTER 10
|
||||
#define MAX_PA_COUNTER 15
|
||||
if (ctx->pa_counter > MAX_PA_COUNTER) {
|
||||
krb5_set_error_message(context, KRB5_GET_IN_TKT_LOOP,
|
||||
N_("Looping %d times while getting "
|
||||
@@ -2296,13 +2613,8 @@ init_creds_step(krb5_context context,
|
||||
NULL);
|
||||
if (ret == 0 && ctx->pk_init_ctx) {
|
||||
PA_DATA *pa_pkinit_kx;
|
||||
int idx = 0;
|
||||
|
||||
pa_pkinit_kx =
|
||||
krb5_find_padata(rep.kdc_rep.padata->val,
|
||||
rep.kdc_rep.padata->len,
|
||||
KRB5_PADATA_PKINIT_KX,
|
||||
&idx);
|
||||
pa_pkinit_kx = find_pa_data(rep.kdc_rep.padata, KRB5_PADATA_PKINIT_KX);
|
||||
|
||||
ret = _krb5_pk_kx_confirm(context, ctx->pk_init_ctx,
|
||||
ctx->fast_state.reply_key,
|
||||
@@ -2344,7 +2656,8 @@ init_creds_step(krb5_context context,
|
||||
/*
|
||||
* Unwrap KRB-ERROR
|
||||
*/
|
||||
ret = fast_unwrap_error(context, &ctx->fast_state, &ctx->error);
|
||||
ret = fast_unwrap_error(context, ctx->nonce, &ctx->fast_state,
|
||||
&ctx->error, &ctx->md);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
@@ -2361,24 +2674,17 @@ init_creds_step(krb5_context context,
|
||||
* more try.
|
||||
*/
|
||||
|
||||
if (ret == KRB5KDC_ERR_PREAUTH_REQUIRED) {
|
||||
|
||||
free_METHOD_DATA(&ctx->md);
|
||||
memset_s(&ctx->md, sizeof(ctx->md), 0, sizeof(ctx->md));
|
||||
|
||||
if (ctx->error.e_data) {
|
||||
ret = decode_METHOD_DATA(ctx->error.e_data->data,
|
||||
ctx->error.e_data->length,
|
||||
&ctx->md,
|
||||
NULL);
|
||||
if (ret)
|
||||
krb5_set_error_message(context, ret,
|
||||
N_("Failed to decode METHOD-DATA", ""));
|
||||
} else {
|
||||
if (ret == KRB5KDC_ERR_PREAUTH_REQUIRED ||
|
||||
ret == KRB5_KDC_ERR_MORE_PREAUTH_DATA_REQUIRED) {
|
||||
if (available_padata_count(&ctx->md) == 0) {
|
||||
krb5_set_error_message(context, ret,
|
||||
N_("Preauth required but no preauth "
|
||||
"options send by KDC", ""));
|
||||
}
|
||||
if (ret == KRB5_KDC_ERR_MORE_PREAUTH_DATA_REQUIRED)
|
||||
ret = fast_validate_conversation(&ctx->md, &ctx->fast_state);
|
||||
else
|
||||
ret = 0;
|
||||
} else if (ret == KRB5KRB_AP_ERR_SKEW && context->kdc_sec_offset == 0) {
|
||||
/*
|
||||
* Try adapt to timeskrew when we are using pre-auth, and
|
||||
@@ -2819,7 +3125,7 @@ krb5_init_creds_get(krb5_context context, krb5_init_creds_context ctx)
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
if ((flags & 1) == 0)
|
||||
if ((flags & KRB5_INIT_CREDS_STEP_FLAG_CONTINUE) == 0)
|
||||
break;
|
||||
|
||||
ret = krb5_sendto_context (context, stctx, &out,
|
||||
@@ -3056,3 +3362,88 @@ krb5_get_init_creds_keytab(krb5_context context,
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
KRB5_LIB_FUNCTION void KRB5_LIB_CALL
|
||||
_krb5_init_creds_set_gss_mechanism(krb5_context context,
|
||||
krb5_gss_init_ctx gssic,
|
||||
const struct gss_OID_desc_struct *gss_mech)
|
||||
{
|
||||
gssic->mech = gss_mech; /* OIDs are interned, so no copy required */
|
||||
}
|
||||
|
||||
KRB5_LIB_FUNCTION const struct gss_OID_desc_struct * KRB5_LIB_CALL
|
||||
_krb5_init_creds_get_gss_mechanism(krb5_context context,
|
||||
krb5_gss_init_ctx gssic)
|
||||
{
|
||||
return gssic->mech;
|
||||
}
|
||||
|
||||
KRB5_LIB_FUNCTION void KRB5_LIB_CALL
|
||||
_krb5_init_creds_set_gss_cred(krb5_context context,
|
||||
krb5_gss_init_ctx gssic,
|
||||
struct gss_cred_id_t_desc_struct *gss_cred)
|
||||
{
|
||||
if (gssic->cred != gss_cred &&
|
||||
(gssic->flags & GSSIC_FLAG_RELEASE_CRED))
|
||||
gssic->release_cred(context, gssic, gssic->cred);
|
||||
|
||||
gssic->cred = gss_cred;
|
||||
gssic->flags |= GSSIC_FLAG_RELEASE_CRED;
|
||||
}
|
||||
|
||||
KRB5_LIB_FUNCTION const struct gss_cred_id_t_desc_struct * KRB5_LIB_CALL
|
||||
_krb5_init_creds_get_gss_cred(krb5_context context,
|
||||
krb5_gss_init_ctx gssic)
|
||||
{
|
||||
return gssic->cred;
|
||||
}
|
||||
|
||||
KRB5_LIB_FUNCTION void KRB5_LIB_CALL
|
||||
_krb5_init_creds_set_gss_context(krb5_context context,
|
||||
krb5_gss_init_ctx gssic,
|
||||
struct gss_ctx_id_t_desc_struct *gss_ctx)
|
||||
{
|
||||
if (gssic->ctx != gss_ctx)
|
||||
gssic->delete_sec_context(context, gssic, gssic->ctx);
|
||||
gssic->ctx = gss_ctx;
|
||||
}
|
||||
|
||||
KRB5_LIB_FUNCTION const struct gss_ctx_id_t_desc_struct * KRB5_LIB_CALL
|
||||
_krb5_init_creds_get_gss_context(krb5_context context,
|
||||
krb5_gss_init_ctx gssic)
|
||||
{
|
||||
return gssic->ctx;
|
||||
}
|
||||
|
||||
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
|
||||
_krb5_init_creds_init_gss(krb5_context context,
|
||||
krb5_init_creds_context ctx,
|
||||
krb5_gssic_step step,
|
||||
krb5_gssic_finish finish,
|
||||
krb5_gssic_release_cred release_cred,
|
||||
krb5_gssic_delete_sec_context delete_sec_context,
|
||||
const struct gss_cred_id_t_desc_struct *gss_cred,
|
||||
const struct gss_OID_desc_struct *gss_mech,
|
||||
unsigned int flags)
|
||||
{
|
||||
krb5_gss_init_ctx gssic = ctx->gss_init_ctx;
|
||||
|
||||
gssic = calloc(1, sizeof(*gssic));
|
||||
if (gssic == NULL)
|
||||
return krb5_enomem(context);
|
||||
|
||||
if (ctx->gss_init_ctx)
|
||||
free_gss_init_ctx(context, ctx->gss_init_ctx);
|
||||
ctx->gss_init_ctx = gssic;
|
||||
|
||||
gssic->cred = (struct gss_cred_id_t_desc_struct *)gss_cred;
|
||||
gssic->mech = gss_mech;
|
||||
gssic->flags = flags;
|
||||
|
||||
gssic->step = step;
|
||||
gssic->finish = finish;
|
||||
gssic->release_cred = release_cred;
|
||||
gssic->delete_sec_context = delete_sec_context;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@@ -901,6 +901,32 @@ can be given as a number followed by a unit, such as
|
||||
.Dq 2d
|
||||
for
|
||||
.Dq two days .
|
||||
.It Li enable_gss_preauth = Va boolean
|
||||
Enables pre-authentication using a GSS-API mechanism supported by the client and KDC.
|
||||
The GSS-API initiator and AS request client names must match, unless the
|
||||
.Li
|
||||
WELLKNOWN/FEDERATED
|
||||
name was used in the AS request, in which case the AS reply will contain the
|
||||
GSS-API initiator name. Authorization and mapping behavior may be customized
|
||||
by plugins. If synthetic clients are enabled, then the GSS-API initiator need
|
||||
not exist in the local database. GSS-API pre-authentication is disabled by
|
||||
default.
|
||||
.It Li enable_gss_auth_data = Va boolean
|
||||
When using GSS-API pre-authentication, includes a Kerberos authorization data
|
||||
element containing naming attributes associated with the GSS-API initiator. This
|
||||
is disabled by default as it may significantly increase the size of returned
|
||||
tickets.
|
||||
.It Li gss_mechanisms_allowed = Va mechs ...
|
||||
A list of GSS-API mechanisms that may be used for GSS-API pre-authentication.
|
||||
.It Li gss_cross_realm_mechanisms_allowed = Va mechs ...
|
||||
A list of GSS-API mechanisms that, when using the default authorization
|
||||
mechanism, will be permitted to map Kerberos principals in foreign realms. The
|
||||
list is empty by default. Initiator names from mechanisms not on this list will
|
||||
be mapped to an enterprise principal in the AS-REQ realm. This option is
|
||||
intended to avoid conflating GSS-API pre-authentication and Kerberos
|
||||
cross-realm authentication. The behavior is provided by the default
|
||||
authorization mechanism and will be overridden by an authorization plugin.
|
||||
Mechanisms may be identified by dot-separated OID or a short name.
|
||||
.It Li historical_anon_realm = Va boolean
|
||||
Enables pre-7.0 non-RFC-comformant KDC behavior.
|
||||
With this option set to
|
||||
|
@@ -715,10 +715,11 @@ typedef EncAPRepPart krb5_ap_rep_enc_part;
|
||||
#define KRB5_WELLKNOWN_NAME ("WELLKNOWN")
|
||||
#define KRB5_ANON_NAME ("ANONYMOUS")
|
||||
#define KRB5_ANON_REALM ("WELLKNOWN:ANONYMOUS")
|
||||
#define KRB5_FEDERATED_NAME ("FEDERATED")
|
||||
#define KRB5_FEDERATED_REALM ("WELLKNOWN:FEDERATED")
|
||||
#define KRB5_WELLKNOWN_ORG_H5L_REALM ("WELLKNOWN:ORG.H5L")
|
||||
#define KRB5_DIGEST_NAME ("digest")
|
||||
|
||||
|
||||
#define KRB5_PKU2U_REALM_NAME ("WELLKNOWN:PKU2U")
|
||||
#define KRB5_LKDC_REALM_NAME ("WELLKNOWN:COM.APPLE.LKDC")
|
||||
|
||||
|
@@ -101,12 +101,13 @@ error_code DIGEST_IN_SIGNED_DATA_NOT_ACCEPTED, "Digest in signedData not accepte
|
||||
error_code PUBLIC_KEY_ENCRYPTION_NOT_SUPPORTED, "Public key encryption not supported"
|
||||
|
||||
## these are never used
|
||||
#index 80
|
||||
#index 85
|
||||
#prefix KRB5_IAKERB
|
||||
#error_code ERR_KDC_NOT_FOUND, "IAKERB proxy could not find a KDC"
|
||||
#error_code ERR_KDC_NO_RESPONSE, "IAKERB proxy never reeived a response from a KDC"
|
||||
|
||||
# 82-93 are reserved
|
||||
index 91
|
||||
error_code MORE_PREAUTH_DATA_REQUIRED, "More pre-authentication data required"
|
||||
|
||||
index 94
|
||||
error_code INVALID_HASH_ALG, "Invalid OTP digest algorithm"
|
||||
|
@@ -140,6 +140,13 @@ struct krb5_dh_moduli;
|
||||
/* v4 glue */
|
||||
struct _krb5_krb_auth_data;
|
||||
|
||||
struct krb5_gss_init_ctx_data;
|
||||
typedef struct krb5_gss_init_ctx_data *krb5_gss_init_ctx;
|
||||
|
||||
struct gss_ctx_id_t_desc_struct;
|
||||
struct gss_cred_id_t_desc_struct;
|
||||
struct gss_OID_desc_struct;
|
||||
|
||||
#include <der.h>
|
||||
|
||||
#include <krb5.h>
|
||||
@@ -152,6 +159,34 @@ struct _krb5_krb_auth_data;
|
||||
|
||||
#include "crypto.h"
|
||||
|
||||
typedef krb5_error_code (KRB5_LIB_CALL *krb5_gssic_step)(
|
||||
krb5_context,
|
||||
krb5_gss_init_ctx,
|
||||
const krb5_creds *,
|
||||
KDCOptions options,
|
||||
krb5_data *,
|
||||
krb5_data *,
|
||||
krb5_data *);
|
||||
|
||||
typedef krb5_error_code (KRB5_LIB_CALL *krb5_gssic_finish)(
|
||||
krb5_context,
|
||||
krb5_gss_init_ctx,
|
||||
const krb5_creds *,
|
||||
krb5int32,
|
||||
krb5_enctype,
|
||||
krb5_principal *,
|
||||
krb5_keyblock **);
|
||||
|
||||
typedef void (KRB5_LIB_CALL *krb5_gssic_release_cred)(
|
||||
krb5_context,
|
||||
krb5_gss_init_ctx,
|
||||
struct gss_cred_id_t_desc_struct *);
|
||||
|
||||
typedef void (KRB5_LIB_CALL *krb5_gssic_delete_sec_context)(
|
||||
krb5_context,
|
||||
krb5_gss_init_ctx,
|
||||
struct gss_ctx_id_t_desc_struct *);
|
||||
|
||||
#include <krb5-private.h>
|
||||
|
||||
#include "heim_threads.h"
|
||||
|
@@ -525,6 +525,7 @@ EXPORTS
|
||||
krb5_principal_get_realm
|
||||
krb5_principal_get_type
|
||||
krb5_principal_is_anonymous
|
||||
krb5_principal_is_federated
|
||||
krb5_principal_is_krbtgt
|
||||
krb5_principal_is_root_krbtgt
|
||||
krb5_principal_match
|
||||
@@ -792,6 +793,15 @@ EXPORTS
|
||||
_krb5_SP800_108_HMAC_KDF
|
||||
_krb5_get_ad
|
||||
|
||||
; Shared with GSSAPI preauth wrapper
|
||||
_krb5_init_creds_set_gss_mechanism
|
||||
_krb5_init_creds_get_gss_mechanism
|
||||
_krb5_init_creds_set_gss_cred
|
||||
_krb5_init_creds_get_gss_cred
|
||||
_krb5_init_creds_set_gss_context
|
||||
_krb5_init_creds_get_gss_context
|
||||
_krb5_init_creds_init_gss
|
||||
|
||||
; Shared with libkadm5
|
||||
_krb5_load_plugins
|
||||
_krb5_unload_plugins
|
||||
|
@@ -1312,6 +1312,28 @@ krb5_principal_is_anonymous(krb5_context context,
|
||||
return strcmp(p->realm, KRB5_ANON_REALM) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true iff name is WELLKNOWN/FEDERATED
|
||||
*
|
||||
* @ingroup krb5_principal
|
||||
*/
|
||||
|
||||
KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
|
||||
krb5_principal_is_federated(krb5_context context,
|
||||
krb5_const_principal p)
|
||||
{
|
||||
if (p->name.name_type != KRB5_NT_WELLKNOWN &&
|
||||
p->name.name_type != KRB5_NT_UNKNOWN)
|
||||
return FALSE;
|
||||
|
||||
if (p->name.name_string.len != 2 ||
|
||||
strcmp(p->name.name_string.val[0], KRB5_WELLKNOWN_NAME) != 0 ||
|
||||
strcmp(p->name.name_string.val[1], KRB5_FEDERATED_NAME) != 0)
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static int
|
||||
tolower_ascii(int c)
|
||||
{
|
||||
|
@@ -522,6 +522,7 @@ HEIMDAL_KRB5_2.0 {
|
||||
krb5_principal_set_realm;
|
||||
krb5_principal_set_type;
|
||||
krb5_principal_is_anonymous;
|
||||
krb5_principal_is_federated;
|
||||
krb5_principal_is_krbtgt;
|
||||
krb5_principal_is_root_krbtgt;
|
||||
krb5_print_address;
|
||||
@@ -784,6 +785,15 @@ HEIMDAL_KRB5_2.0 {
|
||||
_krb5_SP800_108_HMAC_KDF;
|
||||
_krb5_get_ad;
|
||||
|
||||
# Shared with GSSAPI preauth wrapper
|
||||
_krb5_init_creds_set_gss_mechanism;
|
||||
_krb5_init_creds_get_gss_mechanism;
|
||||
_krb5_init_creds_set_gss_cred;
|
||||
_krb5_init_creds_get_gss_cred;
|
||||
_krb5_init_creds_set_gss_context;
|
||||
_krb5_init_creds_get_gss_context;
|
||||
_krb5_init_creds_init_gss;
|
||||
|
||||
# Shared with libkadm5
|
||||
_krb5_load_plugins;
|
||||
_krb5_unload_plugins;
|
||||
|
@@ -136,6 +136,29 @@ echo "Getting service ticket"
|
||||
${kgetcred} ${server}@${R} || { exit 1; }
|
||||
${kdestroy}
|
||||
|
||||
#
|
||||
# Test GSS-API pre-authentication using SAnon. It will only succeed where there
|
||||
# is FAST armor to authenticate the KDC, otherwise it will fail as SAnon does
|
||||
# not provide mutual authentication (GSS_C_MUTUAL_FLAG).
|
||||
#
|
||||
|
||||
for mech in sanon-x25519 spnego ; do
|
||||
echo "Trying ${mech} pre-authentication with FAST armor"; > messages.log
|
||||
${kinit} --fast-armor-cache=${acache} \
|
||||
--anonymous --gss-mech=${mech} @$R 2>/dev/null || \
|
||||
{ ec=1 ; eval "${testfailed}"; }
|
||||
|
||||
echo "Trying ${mech} pre-authentication with anonymous FAST armor"; > messages.log
|
||||
${kinit} --pk-anon-fast-armor \
|
||||
--anonymous --gss-mech=${mech} @$R 2>/dev/null || \
|
||||
{ ec=1 ; eval "${testfailed}"; }
|
||||
|
||||
echo "Trying ${mech} pre-authentication with no FAST armor"; > messages.log
|
||||
${kinit} \
|
||||
--anonymous --gss-mech=${mech} @$R 2>/dev/null && \
|
||||
{ ec=1 ; eval "${testfailed}"; }
|
||||
done
|
||||
|
||||
#
|
||||
# Use MIT client tools
|
||||
#
|
||||
|
@@ -83,6 +83,10 @@
|
||||
enable-http = true
|
||||
|
||||
synthetic_clients = true
|
||||
|
||||
enable_gss_preauth = true
|
||||
gss_mechanisms_allowed = sanon-x25519
|
||||
|
||||
enable-pkinit = true
|
||||
pkinit_identity = FILE:@srcdir@/../../lib/hx509/data/kdc.crt,@srcdir@/../../lib/hx509/data/kdc.key
|
||||
pkinit_anchors = FILE:@srcdir@/../../lib/hx509/data/ca.crt
|
||||
|
Reference in New Issue
Block a user