logo_kerberos.gif

Difference between revisions of "Projects/Acceptor Names"

From K5Wiki
Jump to: navigation, search
 
(11 intermediate revisions by the same user not shown)
Line 1: Line 1:
{{project-early}}
+
{{project-rel|1.10}}
{{project-target|1.10}}
 
   
 
==Overview==
 
==Overview==
Line 7: Line 7:
 
* We will stop using the domain_realm map or default realm to determine the realm of host-based GSS acceptor names. Instead, we will allow any keytab entry matching the other constraints of the host-based name, regardless of realm.
 
* We will stop using the domain_realm map or default realm to determine the realm of host-based GSS acceptor names. Instead, we will allow any keytab entry matching the other constraints of the host-based name, regardless of realm.
 
* If the host-based acceptor name contains a service name but no hostname, we will allow any keytab entry matching the service name, regardless of its hostname or realm.
 
* If the host-based acceptor name contains a service name but no hostname, we will allow any keytab entry matching the service name, regardless of its hostname or realm.
* If the host-based acceptor name contains a service@hostname pair, we will allow a keytab entry matching the service name and either the original or canonicalized hostname. (XXX should we also allow the forward-canonicalized hostname? That has significant implications for the implementation design.)
 
 
* If the new profile variable ignore_acceptor_hostname is set, we will disregard the hostname part of a service@hostname pair and allow any keytab entry matching the service name.
 
* If the new profile variable ignore_acceptor_hostname is set, we will disregard the hostname part of a service@hostname pair and allow any keytab entry matching the service name.
   
Line 33: Line 32:
 
==Implementation Design==
 
==Implementation Design==
   
When we create a krb5 mechanism name from a host-based GSS name, we should record the service name and hostname (if given). If ignore_acceptor_hostname is set in the profile, we should discard the hostname at this time. If a hostname is given and not ignored, we should attempt to canonicalize it and record the canonicalized name as well. (NOTE: this will be the first time the gss-krb5 code directly consults the profile.)
 
  +
We define a new concept "matching principal", which is a principal of type KRB5_NT_SRV_HOST with an empty realm and/or second principal component (hostname).
   
If the name is used on the initiator path (as an initiator name or target name), we should convert from the recorded form to the principal we would construct with today's code.
 
  +
When we create a krb5 mechanism name from a host-based GSS name, we should record the service name and hostname (if given).
   
If the name is imported for an acceptor cred, we should construct one or two partial principals: service/@ (empty hostname and realm) if no hostname was given, service/orighostname@ (empty realm) and service/canonhostname@ (empty realm) if one was given. We should scan the keytab to make sure one of the partial principals is matched by a keytab entry.
 
  +
If the name is used on the initiator path (as an initiator name or target name) or is exported, we should convert from the recorded form to the principal we would construct with today's code, using krb5_sname_to_principal. (UPDATE: it proved to be too difficult to calculate the krb5_sname_to_principal result only when it is needed. Instead, we record the passed-on service and hostname, but also call krb5_sname_to_principal at mechanism name import time, even though in some cases the result won't be used.)
   
When an acceptor cred with an imported host-based name is used to accept a security context, we should try krb5_rd_req with the first partial principal and, if that fails, try again with the second (if there is one). (If the keytab has no iterator function, we should probably just skip this check.)
+
If the name is used in an acceptor cred, we should construct a matching principal service/hostname@ (empty realm) or service/@ (empty hostname and realm) depending on whether a name was recorded. We should scan the keytab to make sure the matching principal is matched by a keytab entry. If the keytab has no iteration support, we should just skip this check.
   
krb5_rd_req should treat partial host-based principals as restrictions on the keytab scan it already performs when no principal is passed in. (If the keytab has no iterator function, then krb5_rd_req should check that the principal asserted by the client matches the partial host-based principal, and look up that principal in the keytab.)
+
When an acceptor cred with an imported host-based name is used to accept a security context, we should try krb5_rd_req with the matching principal. krb5_rd_req should treat matching principals as restrictions on the keytab scan it already performs when no principal is passed in. krb5_rd_req should ignore the hostname component of a matching principal if the ignore_acceptor_hostname profile variable is set. If the keytab has no iterator function, then krb5_rd_req should check that the principal asserted by the client matches the matching principal, and look up that principal in the keytab.
   
We will need new krb5 library interfaces for:
 
  +
We will need a new krb5 library interface for matching a principal against a matching principal, following the same rules as krb5_rd_req will apply. The proposed API for this function is:
   
* Canonicalizing a hostname according to the sn2princ rules, honoring the profile "rdns" setting.
 
  +
krb5_boolean
* Building a principal according to the sn2princ rules, with the canonicalized hostname already known. (sn2princ will be reimplemented in terms of this and the previous interface.)
 
  +
krb5_sname_match(krb5_context context, krb5_const_principal matching, krb5_const_principal princ);
* Matching a principal against a partial principal, following the same rules as krb5_rd_req will apply.
 
   
Proposed APIs for these functions:
 
  +
==Rejected Ideas==
   
krb5_error_code
 
  +
If a server imports a host-based name containing a hostname, we could try the uncanonicalized, forward-canonicalized, and reverse-canonicalized forms of the hostname. Upon discussion of this idea, we decided that it would add significant complexity to the code, and we would be better off encouraging GSS server applications to import just the service name (without the local hostname attached).
krb5_sname_canonhost(krb5_context context, const char *host, const char **canonhost_out);
 
   
krb5_error_code
 
  +
==Future Considerations==
krb5_sname_to_princ_canon(krb5_context context, const char *canonhost, const char *sname, krb5_int32 type,
 
krb5_principal *princ_out);
 
   
krb5_error_code
 
  +
For security reasons, we have long desired to get away from canonicalizing hostnames since it generally uses insecure DNS, but we have been unwilling to break existing deployments by simply turning it off. One possibility is to allow a KDC to assert that it should be responsible for host canonicalization, communicated as a flag stored in the ccache.
krb5_sname_match(krb5_context context, krb5_const_principal partial, krb5_const_principal full);
 
   
==Future Considerations==
 
  +
The changes proposed in this project should further that goal. By recording the original hostname when a host-based name is imported into the mechanism, we make it possible for the GSSAPI initiator code to use that information if the ccache indicates that it should.
  +
  +
==Test Plan==
  +
  +
Python tests and a simple GSS initiator/acceptor C harness will be created to test each aspect of the new behavior.
  +
  +
==Release Notes==
  +
  +
Developer experience:
  +
  +
* Add the ability for GSSAPI servers to use any keytab key for a specified service, if the server specifies a host-based name with no hostname component.
   
For security reasons, we have long desired to get away from canonicalizing hostnames since it generally uses insecure DNS, but we have been unwilling to break existing deployments by simply turning it off. One possibility is to allow a KDC to assert that it should be responsible for host canonicalization, communicated as a ticket flag which would be stored in the credentials cache.
 
  +
Administrator experience:
   
The implementation design of this project could, as a side effect, allow a GSSAPI initiator to honor this ticket flag.
 
  +
* Add the profile variable ignore_acceptor_hostname in libdefaults. If set, GSSAPI will ignore the hostname component of acceptor names supplied by the server, allowing any keytab key matching the service to be used.
The initiator would consult the ticket cache and simply avoid calling krb5_sname_canonhost() before calling
 

Latest revision as of 14:19, 12 October 2011

This project was completed in release 1.10.


Overview

The goal of this project is to improve the likelihood that a GSS server application will be able to use the correct entries from a keytab when accepting authentication. Specifically:

  • We will stop using the domain_realm map or default realm to determine the realm of host-based GSS acceptor names. Instead, we will allow any keytab entry matching the other constraints of the host-based name, regardless of realm.
  • If the host-based acceptor name contains a service name but no hostname, we will allow any keytab entry matching the service name, regardless of its hostname or realm.
  • If the new profile variable ignore_acceptor_hostname is set, we will disregard the hostname part of a service@hostname pair and allow any keytab entry matching the service name.

It is not required that this new flexibility extend to non-GSS krb5 servers, although it is acceptable if it flows naturally from the implementation.

Current Behavior

A GSSAPI server may invoke gss_accept_context with GSS_C_NO_CREDENTIAL, or with a credential acquired with GSS_C_NO_NAME, then we place no restrictions on the keytab entry used to decrypt the sender's ticket; we scan each keytab entry until we find one which can decrypt it. No changes are proposed in this case.

If a server calls gss_import_name with the name type GSS_C_NT_HOSTBASED_SERVICE, it supplies a service name and optionally a hostname. When a credential is acquired with this name, the mechglue will invoke krb5_gss_import_name() to create the mechanism name. This function calls krb5_sname_to_principal() to construct the principal, passing NULL for the hostname if none was supplied. krb5_sname_to_principal() does the following:

  1. If the hostname is NULL, calls gethostname() to get an initial value for the hostname.
  2. Canonicalizes the hostname using getaddrinfo() and possibly getnameinfo().
  3. Attempts to map the hostname to a realm using krb5_get_host_realm(). This will consult the profile domain_realm map, and will return the referral (empty) realm if no mapping exists.
  4. Constructs a principal servicename/canonicalized-hostname@realm.

When acceptor credentials are acquired using the mechanism name, acquire_accept_cred() looks up the principal in the host's keytab, and returns an error if it is not found. The keytab lookup code translates from the empty realm to the default realm.

When gss_accept_sec_context() is invoked, krb5_rd_req() looks up the principal in the host's keytab again, and uses it to decrypt the ticket.

Note that the principal resulting from krb5_sname_to_principal() may be used in the initiator code path as either an initiator or target name. If used as an initiator name, it is looked up in the credential cache, or passed to krb5_get_init_creds_password(). If used as a target name, it is passed to krb5_get_credentials() as the server name.

The principal resulting from krb5_sname_to_principal() may also be used by gss_export_name(). RFC 2744 section 5.5 mandates that gss_canonicalize_name and gss_export_name produce the same result as would be produced by using the name as an initiator name, establishing a security context, and exporting the authenticated name on the acceptor. There is no requirement that canonicalize/export match the result of accepting a security context with an acceptor name and querying the acceptor name of the resulting context.

Implementation Design

We define a new concept "matching principal", which is a principal of type KRB5_NT_SRV_HOST with an empty realm and/or second principal component (hostname).

When we create a krb5 mechanism name from a host-based GSS name, we should record the service name and hostname (if given).

If the name is used on the initiator path (as an initiator name or target name) or is exported, we should convert from the recorded form to the principal we would construct with today's code, using krb5_sname_to_principal. (UPDATE: it proved to be too difficult to calculate the krb5_sname_to_principal result only when it is needed. Instead, we record the passed-on service and hostname, but also call krb5_sname_to_principal at mechanism name import time, even though in some cases the result won't be used.)

If the name is used in an acceptor cred, we should construct a matching principal service/hostname@ (empty realm) or service/@ (empty hostname and realm) depending on whether a name was recorded. We should scan the keytab to make sure the matching principal is matched by a keytab entry. If the keytab has no iteration support, we should just skip this check.

When an acceptor cred with an imported host-based name is used to accept a security context, we should try krb5_rd_req with the matching principal. krb5_rd_req should treat matching principals as restrictions on the keytab scan it already performs when no principal is passed in. krb5_rd_req should ignore the hostname component of a matching principal if the ignore_acceptor_hostname profile variable is set. If the keytab has no iterator function, then krb5_rd_req should check that the principal asserted by the client matches the matching principal, and look up that principal in the keytab.

We will need a new krb5 library interface for matching a principal against a matching principal, following the same rules as krb5_rd_req will apply. The proposed API for this function is:

 krb5_boolean
 krb5_sname_match(krb5_context context, krb5_const_principal matching, krb5_const_principal princ);

Rejected Ideas

If a server imports a host-based name containing a hostname, we could try the uncanonicalized, forward-canonicalized, and reverse-canonicalized forms of the hostname. Upon discussion of this idea, we decided that it would add significant complexity to the code, and we would be better off encouraging GSS server applications to import just the service name (without the local hostname attached).

Future Considerations

For security reasons, we have long desired to get away from canonicalizing hostnames since it generally uses insecure DNS, but we have been unwilling to break existing deployments by simply turning it off. One possibility is to allow a KDC to assert that it should be responsible for host canonicalization, communicated as a flag stored in the ccache.

The changes proposed in this project should further that goal. By recording the original hostname when a host-based name is imported into the mechanism, we make it possible for the GSSAPI initiator code to use that information if the ccache indicates that it should.

Test Plan

Python tests and a simple GSS initiator/acceptor C harness will be created to test each aspect of the new behavior.

Release Notes

Developer experience:

  • Add the ability for GSSAPI servers to use any keytab key for a specified service, if the server specifies a host-based name with no hostname component.

Administrator experience:

  • Add the profile variable ignore_acceptor_hostname in libdefaults. If set, GSSAPI will ignore the hostname component of acceptor names supplied by the server, allowing any keytab key matching the service to be used.