Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Present the name provided by the caller for GSSAPI/SASL authentication #21

Closed
wants to merge 2 commits into from

2 participants

Michael Komitee Peter Marschall
Michael Komitee

In commit af63067 on 2008-04-21 there was an attempt to "Fix a problem with Net::LDAP when talking to a round-robin LDAP server(s) using SASL/GSSAPI authentication to use the provided hostname not the canonical name"

This commit changed Net::LDAP so that it passes an IP Address to SASL/GSSAPI as the server identification instead of the server name supplied by the client. This assumes that Kerberos (the usual GSSAPI mechanism employed) will do a reverse DNS lookup to construct the Kerberos service principal name, since IP Addresses are almost never used for this. This need not be the case; MIT Kerberos, for example, can be configured to resolve CNAME and PTR records, just CNAMEs, or perform no DNS canonicalization at all.

Since we've configured Kerberos to not do PTR lookups, Net::LDAP with SASL/GSSAPI does not work in our environment.

The problem here is in assuming that it's even possible for Net::LDAP to (as the Changes explanation says) "Pass correct hostname to SASL when connecting to a round-robin" There's no real way for the library to know what the "correct hostname" is supposed to be, beyond that specified by the caller. It's the responsibility of the systems administrator to ensure that Kerberos, DNS records, service principals, and application configuration are arranged such that they all work together, and if a library tries to second-guess this by altering the name passed to it, it will likely cause this kind of breakage at some point. As a default, a library should pass the hostname unaltered to SASL/GSSAPI so that it can obey the local rules for locating the correct service principal.

This isn't only a functional problem, it could be considered a security problem. DNS is insecure, but mutual authentication with Kerberos ensures that we can trust the server to which we connect and successfully authenticate. If we use the same (possibly compromised / spoofed) DNS system to find our server and construct the service name we use for authentication, we lose that benefit from mutual authentication. If the LDAP server is used to provide passwd/group maps for unix hosts, the results could be catastrophic.

On the other hand, sometimes we face the opposite problem -- getting Kerberos to work in a misconfigured environment where the rules in effect produce the wrong result. In that case it may be useful to offer the library client the option of performing some DNS canonicalization on its own, or alternately allow the client to specify the Kerberos principal directly -- but these should be options, not the default.

This patch reverts the behavior to using the name supplied by the caller instead of using the address to which a connection has been established. This will reliably produce the service name that the caller intended to authenticate. It also provides a new (documented) OPTION to bind, "sasl_authenticate_with" which if set to the (case insensitive) string "CONNECTED_IP", enables the current behavior, and if set to the (case insensitive string) "CONNECTED_NAME", performs the reverse dns lookup on the peeraddr (for environments which have kerberos configured to not perform reverse lookups).

I understand that changing the default behavior like this is not 100% backwards compatible and that there are some users whose programs may break as a result, but in my opinion the security ramifications of this behavior warrant some consideration.

mkomitee added some commits
Michael Komitee mkomitee Present the name provided by the caller for GSSAPI/SASL authentication
Reverts a change from af63067 which attempted to work around the use of
round-robin A records by always passing the IP address of the LDAP server to
SASL/GSSAPI which caused a reliance on Kerberos DNS canonicalization to guess
the correct service name for authentication. This causes problems with Kerberos
configurations that do not perform DNS canonicalization -- the service name
cannot be determined despite the fact that it was correctly provided by the
caller.

Provides for a new option which can be passed to bind which will force us to
continue to use the old behavior, but by default, uses the hostname as provided
by the caller for authentication purposes.

To reproduce the old behavior, instead of:

    $ldap->bind(...)

Use:

    $ldap->bind(... sasl_authenticate_with => 'ip')
ef43ec1
Michael Komitee mkomitee Adding more sasl authentication options
Renamed the 'sasl_authenticate_with' value of 'ip' to 'connected_ip' and added
another 'sasl_authenticate_with' value of 'connected_name' which will cause us
to perform the reverse dns lookup for environments where the underlying kerberos
environment is configured to not perform them.
915ed09
Peter Marschall
Owner

Hi,

sorry for the long delay in answering.
Thanks for the pull request.

I think this issue has been fixed with commit f36b2cd in perl-ldap 0.57.

If it hasn't please feel free to open another pull request.

Best
PEter

Peter Marschall marschap closed this
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on May 30, 2013
  1. Michael Komitee

    Present the name provided by the caller for GSSAPI/SASL authentication

    mkomitee authored
    Reverts a change from af63067 which attempted to work around the use of
    round-robin A records by always passing the IP address of the LDAP server to
    SASL/GSSAPI which caused a reliance on Kerberos DNS canonicalization to guess
    the correct service name for authentication. This causes problems with Kerberos
    configurations that do not perform DNS canonicalization -- the service name
    cannot be determined despite the fact that it was correctly provided by the
    caller.
    
    Provides for a new option which can be passed to bind which will force us to
    continue to use the old behavior, but by default, uses the hostname as provided
    by the caller for authentication purposes.
    
    To reproduce the old behavior, instead of:
    
        $ldap->bind(...)
    
    Use:
    
        $ldap->bind(... sasl_authenticate_with => 'ip')
  2. Michael Komitee

    Adding more sasl authentication options

    mkomitee authored
    Renamed the 'sasl_authenticate_with' value of 'ip' to 'connected_ip' and added
    another 'sasl_authenticate_with' value of 'connected_name' which will cause us
    to perform the reverse dns lookup for environments where the underlying kerberos
    environment is configured to not perform them.
This page is out of date. Refresh to see the latest.
Showing with 39 additions and 9 deletions.
  1. +18 −5 lib/Net/LDAP.pm
  2. +21 −4 lib/Net/LDAP.pod
23 lib/Net/LDAP.pm
View
@@ -424,15 +424,28 @@ sub bind {
# If we're talking to a round-robin, the canonical name of
# the host we are talking to might not match the name we
# requested
- my $connected_name;
- if ($ldap->{net_ldap_socket}->can('peerhost')) {
- $connected_name = $ldap->{net_ldap_socket}->peerhost;
+ # In that case passing sasl_authenticate_with => 'connected_ip'
+ # into bind allows us to pass the IP address of our peer to
+ # sasl so that it can perform a reverse dns lookup to determine
+ # the name against we wish to authenticate.
+ # It's also true that some kerberos installations disable
+ # reverse dns lookups (e.g. mit rdns=no).
+ # In that case passing sasl_authenticate_with => 'connected_name'
+ # into bind will cause us to perform our own DNS resolution of the
+ # IP address of our peer, and pass that to sasl.
+ my $service_name = $ldap->{net_ldap_host};
+ if (exists $arg->{sasl_authenticate_with}) {
+ if (lc($arg->{sasl_authenticate_with}) eq 'connected_ip') {
+ $service_name = $ldap->{net_ldap_socket}->peerhost;
+ } elsif (lc($arg->{sasl_authenticate_with}) eq 'connected_name') {
+ my $iaddr = inet_aton($ldap->{net_ldap_socket}->peerhost);
+ $service_name = gethostbyaddr($iaddr, AF_INET);
+ }
}
- $connected_name ||= $ldap->{net_ldap_host};
$sasl_conn = eval {
local ($SIG{__DIE__});
- $sasl->client_new('ldap', $connected_name);
+ $sasl->client_new('ldap', $service_name);
};
}
else {
25 lib/Net/LDAP.pod
View
@@ -309,10 +309,27 @@ of L<Authen::SASL> or an L<Authen::SASL> client connection by calling
C<client_new> on an L<Authen::SASL> object.
If passed an L<Authen::SASL> object then C<client_new> will be
-called to create a client connection object. The hostname passed
-by C<Net::LDAP> to C<client_new> is the result of calling C<peerhost>
-on the socket. If this is not correct for your environment, consider
-calling C<client_new> and passing the client connection object.
+called to create a client connection object.
+
+The hostname passed by C<Net::LDAP> to C<client_new> will be the
+original hostname used to establish the connection. This can be
+overridden with the C<sasl_authenticate_with> option.
+
+
+=item sasl_authenticate_with =E<gt> TYPE
+
+When binding with a SASL mechanism, if set to the string
+C<CONNECTED_IP>, the hostname passed by C<Net::LDAP> to C<client_new>
+will be the result of calling C<peerhost> on the socket.
+
+When binding with a SASL mechanism, if set to the string
+C<CONNECTED_NAME>, the hostname passed by C<Net::LDAP> to C<client_new>
+will be the result of performing a reverse dns lookup on the
+C<peerhost> on the socket.
+
+If neither of these nor the default is correct for your environment,
+consider calling C<client_new> and passing the client connection
+object.
=back
Something went wrong with that request. Please try again.