How Shibboleth is used to authenticate PPKE users

Zimbra has a mechanism called preauth which makes it possible to interface with other authentication systems.

The big picture

A Shibboleth SP with apache is needed. This authenticates the user with shibboleth and relays it to Zimbra.

In our case shibboleth attribute eduPersonPrincipalName is the same as the email address in Zimbra. The identity provider releases this attribute to the service provider. There a Perl CGI script accesses this as an environment variable. After a valid shibboleth session is established with the SP the user is redirected to a Zimbra URL with a GET variable encoding a signed cookie which identifies the user to Zimbra. This cookie is validated and user access is granted if found valid.

Shibboleth SP setup

From the apache side this is the main part of the configuration. This is needed for the directory containing our custom cgi script. (shibboleth2.xml is needed as usual)

<Directory /where-your-cgi-is>
  AuthType shibboleth
  require shibboleth
  ShibRequireSession On
</Directory>

The cgi script

First the shared secret must be obtained with zmprov:

prov> gdpak example.org

This value is required for the script.

#!/usr/bin/perl

use strict;
use warnings;

use Digest::HMAC_SHA1 qw(hmac_sha1 hmac_sha1_hex);
use Data::Dump qw(pp);
use CGI;

my $cgi = new CGI->new;

my %k;

#prov> gdpak example.org
#preAuthKey: aaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbccccccccccccccccc

$k{"example.org"}="aaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbccccccccccccccccc";

my $zmda = $ENV{"eduPersonPrincipalName"};
my $urlbase = "mail.example.org";

unless($k{$domain}) {
  print $cgi->header(-status=>'500 Service Unavailable');
  print "unknown domain, are you authorized for zimbra?";
  die;
}

my $redir = buildZimbraPreAuthUrl($k{$domain},
            "https://$urlbase/service/preauth",
            $zmda,
            "name");

print $cgi->redirect( -uri => $redir ); 

## @method private string buildZimbraPreAuthUrl(string key, string url, string account, string by)
# Build Zimbra PreAuth URL
# @param key PreAuthKey
# @param url URL
# @param account User account
# @param by Account type
# @return Zimbra PreAuth URL
sub buildZimbraPreAuthUrl {
  my ( $key, $url, $account, $by ) = splice @_;

  # Expiration time
  my $expires = 0;

  # Timestamp
  my $timestamp = time() * 1000;

  # Compute preauth value
  my $computed_value =
    hmac_sha1_hex( "$account|$by|$expires|$timestamp", $key );

  # Build PreAuth URL
  my $zimbra_url;
  $zimbra_url .= $url;
  $zimbra_url .= '?account=' . $account;
  $zimbra_url .= '&by=' . $by;
  $zimbra_url .= '&timestamp=' . $timestamp;
  $zimbra_url .= '&expires=' . $expires;
  $zimbra_url .= '&preauth=' . $computed_value;

  return $zimbra_url;
}

Zimbra can redirect unauthenticated users to our cgi

We can achive that with changing the domain attribute zimbraWebClientLoginURL.