Home  >  Q&A  >  body text

Passwordless LDAP login and get user information using Kerberos ticket in PHP

I'm trying to implement SSO on some of our company's intranet sites using FreeIPA/Kerberos. But there is very little information on this topic.

I have three machines running on my test network:

  1. FreeIPA v4.9.8 Server on Centos 8 Stream
  2. Web Server on Debian 11 (Apache v2.4.53, PHP v7.4.28)
  3. Xubuntu 22.04 Client with Kinit and Firefox

Kinit, Unix login and Apache Kerberos Auth work. The Firefox browser on the client system can log into FreeIPA WebConfig without a password (using Kerberos Ticket). I now want to move this functionality to our intranet page. Until now, logins to these pages have been based on traditional LDAP logins. With a slight adjustment to the login script, users can now log in to the new FreeIPA server. However, he still needs a password, but thanks to the Kerberos ticket, the password is actually no longer required.

The question is, what does passwordless login look like?

Functional snippet of login script:

<?php
$username = $_SERVER['PHP_AUTH_USER'];
$password = 'password';

$ldap_rdn  = 'uid='.$username.',cn=users,cn=accounts,dc=exampletest,dc=de';
$ldap_server = ldap_connect('ldap://ipa.exampletest.de:389');

ldap_set_option($ldap_server, LDAP_OPT_PROTOCOL_VERSION, 3);
ldap_set_option($ldap_server, LDAP_OPT_REFERRALS, 0);

if ($ldap_server) {
  $ldap_bind = @ldap_bind($ldap_server, $ldap_rdn, $password);
  if ($ldap_bind) {
    $search = array("uid","givenname","sn","mail","uidnumber","gidnumber");
    $result = ldap_search($ldap_server, $ldap_rdn, "mail=$username*", $search);
    $info = ldap_get_entries($ldap_server, $result);

    print_r($info);
  }
}
?>

Now I have two thoughts:

  1. I could use ldap_sasl_bind() instead of ldap_bind(), but that function is not documented on php.net (https://www.php.net/manual/en/function.ldap-sasl-bind.php) . If anyone knows how to use this feature I would be grateful.
  2. I would also be happy if I could somehow run ldap_search() without a password to get user information (full name, email, etc.).

Thank you very much in advance.

edit:

Both the Web server VM and the client VM are initialized via "ipa-client-install". In addition, the web server has registered the apache service (ipa service-add HTTP/ebook.exampletest.de).

The apache configuration also reflects this:

<Directory /var/www/ebook/>
        AuthType                GSSAPI
        AuthName                "eBook Login"
        GssapiCredStore         keytab:/etc/apache2/http.keytab
        GssapiAllowedMech       krb5
        GssapiBasicAuthMech     krb5
        GssapiImpersonate       On
        GssapiDelegCcacheDir    /run/apache2/clientcaches
        GssapiLocalName         On
        
        # for production set to on:
        GssapiSSLonly           Off
        GssapiNegotiateOnce     Off
        
        GssapiUseSessions       On
        Session                 On
        SessionCookieName       gssapi_session path=/private;httponly;secure;
        Require                 valid-user
    </Directory>
As I already mentioned, user authentication seems to work like this (client (own ticket) > web service (own ticket) > ipa server ). Otherwise the apache server won't return my ldap/kerberos username. Or am I missing something important here? Is there any other way to enforce this authentication?

Output: <?php print_r($_SERVER) ?>(Intercepted)

[GSS_MECH] => Negotiate/krb5
[GSS_NAME] => test@EXAMPLETEST.DE
[REMOTE_USER] => test
[AUTH_TYPE] => Negotiate
[PHP_AUTH_USER] => test

P粉071743732P粉071743732230 days ago460

reply all(1)I'll reply

  • P粉614840363

    P粉6148403632024-03-27 09:46:20

    Make sure your web server has a correct Kerberos ticket.

    Typically, Kerberos authentication only transmits tickets that are valid only for that server, rather than a blanket of "all" tickets. When a client authenticates to your web app, all you get is a ticket for HTTP/webapp.example.tld, and you can't actually use it to access LDAP on behalf of the user.

    If you need to access LDAP on behalf of a user, you have the following options:

    • Network applications can have their own LDAP directory credentials. This is probably the easiest way. Web applications can use standard password binding or Kerberos (SASL) binding using their own tickets obtained from a keytab.

      • LDAP also supports "impersonation", where the web application will use its own credentials for authentication, but also specify an "Authorization ID" (authzid) to determine which account you will get permission.

        For example, if you authenticate as "webapp" but specify the authzid "myuser" (and if the LDAP server allows it ), then you will get the permissions that "myuser" would normally have – instead of the “webapp” ones.

    • HTTP Negotiation (SPNEGO) authentication for web applications can enable "delegation". Delegation does transmit the master krbtgt ticket to the web server, which then places it in a temporary ticket cache and makes it available to your web application environment.

      However, there are some problems with delegation:

      1. This will slow down every HTTP request because the client must request a new krbtgt ticket with the "forward" flag (unless the web server can use e.g. cookies to avoid the request negotiating the identity authentication) for further requests, e.g. mod_auth_gssapi with "session" mode).

      2. It requires the web application to be highly trusted because it will store wildcard tickets for every user who accesses it (including administrators) - even if the web application itself is trusted not to abuse them, they can still be Steal the server.

      3. Most APIs that use Kerberos (including ldap_sasl_bind()) expect the KRB5CCNAME environment variable to point to the ticket cache. But environment variables are process-scoped, so they can be leaked on unrelated requests whenever PHP reuses the same process (or worse, if you use mod_php to run your web application inside the Apache process).

      In AD, this is specifically called "unconstrained delegation" since AD ​​introduced additional variations.

    • Network applications can use S4U2Proxy aka "constrained delegation" to create tickets on behalf of users for certain limited sets of services (e.g. FreeIPA can restrict them to only accessldap/foo.example.com).

      This is a bit complicated (PHP doesn't have an API for this - you may need to generate a kinit with the correct flags), and still has the same potential issues as the KRB5CCNAME cross-request leak. < /p>

    For regular Kerberos authentication, the usage is as follows:

    ldap_sasl_bind($conn, null, null, "GSSAPI");

    That’s all. The GSSAPI SASL mechanism expects the environment to already have a Kerberos ticket available (e.g. via $KRB5CCNAME or via gss-proxy), and it will authenticate using any ticket found there.

    If you want to use impersonation (assuming it is set up in the LDAP server), you must specify the authz_id:

    ldap_sasl_bind($conn, null, null, "GSSAPI", null, null, $theuser);

    Most ldap_*() PHP functions are direct wrappers around the C libldap library, so its documentation can be used as a partial reference.

    It appears that your example already specifies the exact DN of the user, so additional filtering via mail does not seem to be required - just use objectClass=* specific when reading the mail DN. Also, when you want to read a specific DN, use ldap_read() for a "basic" search rather than a subtree search.

    No, that's not what happened. Your username (i.e. the client Kerberos principal) is stored in the client ticket, so the web server knows it immediately after decrypting the ticket without having to talk to IPA.

    reply
    0
  • Cancelreply